diff --git a/.asf.yaml b/.asf.yaml new file mode 100644 index 00000000000..ac29efed9ff --- /dev/null +++ b/.asf.yaml @@ -0,0 +1,37 @@ +# 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. + +notifications: + commits: commits@cassandra.apache.org + issues: commits@cassandra.apache.org + pullrequests: pr@cassandra.apache.org + jira_options: link worklog + +github: + description: "Java Driver for Apache Cassandra®" + homepage: https://cassandra.apache.org/ + enabled_merge_buttons: + squash: false + merge: false + rebase: true + features: + wiki: false + issues: false + projects: false + autolink_jira: + - CASSANDRA + - CASSJAVA diff --git a/.github/workflows/publish-docs-gp-pages.yml b/.github/workflows/publish-docs-gp-pages.yml new file mode 100644 index 00000000000..57851d517c0 --- /dev/null +++ b/.github/workflows/publish-docs-gp-pages.yml @@ -0,0 +1,101 @@ +name: publish-docs-gh-pages + +on: + workflow_dispatch: + +jobs: + build-docs: + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + # 1. Checkout doc branch + - name: Checkout current branch + uses: actions/checkout@v4 + with: + path: java-driver + + # 2. Java 8 + - name: Set up Java 8 + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: "8" + cache: maven + + # 3. Python 3.10.19 + MkDocs + plugins + - name: Set up Python 3.10.19 + uses: actions/setup-python@v5 + with: + python-version: "3.10.19" + + - name: Install MkDocs dependencies + run: | + python -m pip install --upgrade pip + pip install \ + mkdocs \ + mkdocs-material \ + mkdocs-awesome-pages-plugin \ + mkdocs-macros-plugin \ + mike + + # 4. Build docs via build-doc.sh + - name: Build documentation + working-directory: java-driver + run: | + chmod +x ./build-doc.sh + ./build-doc.sh + + # 5. Checkout gh-pages branch + - name: Checkout GH pages branch + uses: actions/checkout@v4 + with: + ref: gh-pages + path: gh-pages + + - name: Copy and version documentation + working-directory: gh-pages + run: | + git config --global user.email "gha@cassandra.apache.org" + git config --global user.name "GHA for Apache Cassandra Website" + + cd ../java-driver + # lookup current project version + release_version=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout | cut -d- -f1) + # update links to contain version prefix + mike deploy --update-aliases $release_version + # copy documentation web page folder + cd ../gh-pages + cp -r ../java-driver/docs ./ + rm -rf $release_version + mv docs $release_version + + # update latest symlink + rm latest + ln -s $release_version latest + + # remove latest tag + sed -i 's/\"latest\"//g' versions.json + # remove already present line if exists + sed -i '/'"$release_version"'/d' versions.json + # insert new version at the beginning + sed -i '2s/^/ { "version": "'"$release_version"'", "title": "'"$release_version"'", "aliases": ["latest"] },\'$'\n/g' versions.json + + echo "release_version=$release_version" >> "$GITHUB_ENV" + + - name: Commit and push documentation + working-directory: gh-pages + run: | + git config --global user.email "gha@cassandra.apache.org" + git config --global user.name "GHA for Apache Cassandra Website" + + git add . + + if git diff --cached --quiet; then + echo "No changes to push to gh-pages" + exit 0 + fi + + git commit -m "Update generated docs for release ${release_version}" + git push origin gh-pages diff --git a/.gitignore b/.gitignore index eaf1a9ef8b2..07449882cc0 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ .project .java-version +.flattened-pom.xml .documenter_local_last_run /docs diff --git a/.snyk b/.snyk new file mode 100644 index 00000000000..a081b17225c --- /dev/null +++ b/.snyk @@ -0,0 +1,35 @@ +# 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. +# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities. +version: v1.22.2 +# ignores vulnerabilities until expiry date; change duration by modifying expiry date +ignore: + SNYK-JAVA-ORGGRAALVMSDK-2767964: + - '*': + reason: cannot upgrade to graal-sdk 22.1.0+ until we move off Java8, which is slated for later this year + expires: 2024-01-10T00:00:00.000Z + created: 2023-06-21T00:00:00.000Z + SNYK-JAVA-ORGGRAALVMSDK-2769618: + - '*': + reason: cannot upgrade to graal-sdk 22.1.0+ until we move off Java8, which is slated for later this year + expires: 2024-01-10T00:00:00.000Z + created: 2023-06-21T00:00:00.000Z + SNYK-JAVA-ORGGRAALVMSDK-5457933: + - '*': + reason: cannot upgrade to graal-sdk 22.1.0+ until we move off Java8, which is slated for later this year + expires: 2024-01-10T00:00:00.000Z + created: 2023-06-21T00:00:00.000Z diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index a7f970a8c20..00000000000 --- a/.travis.yml +++ /dev/null @@ -1,20 +0,0 @@ -language: java -sudo: false -# see https://sormuras.github.io/blog/2018-03-20-jdk-matrix.html -matrix: - include: - # 8 - - env: JDK='OpenJDK 8' - jdk: openjdk8 - # 11 - - env: JDK='OpenJDK 11' - # switch to JDK 11 before running tests - before_script: . $TRAVIS_BUILD_DIR/ci/install-jdk.sh -F 11 -L GPL -before_install: - # Require JDK8 for compiling - - jdk_switcher use openjdk8 -install: mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V -script: mvn test -Djacoco.skip=true -B -V -cache: - directories: - - $HOME/.m2 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d8d7a4c778a..d15d5921e82 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,23 +1,190 @@ -# Contributing guidelines + + +# Contributing to the Apache Cassandra Java Driver + +Thank you for your interest in contributing! + +## Table of Contents +- [Ways to Contribute](#ways-to-contribute) +- [Contribution Process](#contribution-process) + - [Reporting Issues](#reporting-issues) + - [Submitting Changes (Pull Requests)](#submitting-changes-pull-requests) +- [Development Setup](#development-setup) + - [Prerequisites](#prerequisites) + - [Building the Project](#building-the-project) + - [Running Tests](#running-tests) +- [Coding Guidelines](#coding-guidelines) + - [General](#general) + - [Code Formatting and License Headers](#code-formatting-and-license-headers) + - [Javadoc](#javadoc) + - [Logging](#logging) + - [Don't abuse the stream API](#dont-abuse-the-stream-api) + - [Never assume a specific format for toString()](#never-assume-a-specific-format-for-tostring) + - [Concurrency annotations](#concurrency-annotations) + - [Nullability annotations](#nullability-annotations) +- [Coding Guidelines for Tests](#coding-guidelines-for-tests) + - [Coding style -- test code](#coding-style----test-code) + - [Unit tests](#unit-tests) + - [Integration tests](#integration-tests) + +## Ways to Contribute + +There are many ways to contribute, including: + +- **Bug Reports**: Identify incorrect behavior, inconsistencies, or regressions in the driver. Provide reproduction steps when possible. +- **Feature Requests**: Propose improvements or new functionality. Please describe the use case (not just a proposed API). +- **Documentation Improvements**: Enhance guides, examples, javadocs, or configuration explanations. +- **Pull Requests**: Submit fixes, enhancements, performance improvements, or refactorings. +- **Testing Contributions**: Add missing tests, improve coverage, or enhance test infrastructure. +- **Support & Triage**: Help evaluate reported issues or contribute to discussions. +- **Verify Releases**: Verify the release artifacts work correctly in your environment, when a release is proposed in the mailing list. + +### Communication +1. **Mailing Lists**: Mail to user-subscribe@cassandra.apache.org or dev-subscribe@cassandra.apache.org to join the user@cassandra.apache.org or dev@cassandra.apache.org mailing lists. +2. **Slack**: [#cassandra-drivers](https://the-asf.slack.com/archives/C05LPRVNZV1) channel in the Apache Software Foundation [Slack](https://infra.apache.org/slack.html). You can ask for an invite to the ASF Slack workspace in the mailing lists. +3. **JIRA**: https://issues.apache.org/jira/projects/CASSJAVA +4. **GitHub Repository**: https://github.com/apache/cassandra-java-driver + + +## Contribution Process + +### Reporting Issues + +All issues must be tracked in **Apache JIRA**: + + +When filing an issue: + +- Clearly describe the problem, expected behavior, and actual behavior. +- Include driver version, Java version, and Cassandra cluster details. +- Add reproduction steps or a minimal test case if possible. +- Use the appropriate issue type (Bug, Improvement, New Feature, etc.). +- Set the correct components (e.g., `core`, `mapper-runtime`, `quarkus`). + +Committers will help refine the ticket if needed. + +### Submitting Changes (Pull Requests) + +All code changes require: + +1. **A corresponding JIRA ticket** unless it's a ninja fix. + Include the JIRA key in the PR title, e.g.: + `CASSJAVA-40: Driver testing against Java 21` + +2. **A pull request on GitHub** + Repository: + +3. **Tests** + Every fix or feature should include or update tests. PRs without tests are rarely accepted. + +4. **Documentation updates** + Update manual, javadocs, examples, or reference docs when applicable. + +5. **Passing CI** + PRs must pass all CI jobs unless reviewers explicitly allow exceptions. + +6. **Code review** + Committers will review your changes. 2 approvals from committers are required for merging. -## Code formatting +7. **Squash** + When the PR is ready to merge, use `git rebase -i` to squash your changes into a single commit before merging. + The commit message should follow the format of + ```txt + + patch by ; reviewed by and + ``` + For example, + ```txt + CASSJAVA-108 Update ESRI (and remove org.json) dependencies + patch by Bret McGuire; reviewed by Bret McGuire and Lukasz Antoniak + ``` -We follow the [Google Java Style Guide](https://google.github.io/styleguide/javaguide.html). See -https://github.com/google/google-java-format for IDE plugins. The rules are not configurable. +**Do not** mix unrelated changes in one PR—keep contributions focused. -The build will fail if the code is not formatted. To format all files from the command line, run: - -``` -mvn fmt:format -``` +**Do not** base a PR on another one. + +**Do not** squash commits before the PR is ready to merge. + +--- + +## Development Setup + +### Prerequisites + +- **Java 8+** +- **Maven 3.8.1+** + +### Building the Project -Some aspects are not covered by the formatter: +- Ensure Maven is installed and you are using Java 8. +- Build the project with: + ``` + mvn clean package -DskipTests + ``` +- If using an IDE like IntelliJ and encountering issues with guava-shaded classes: + - Run: + ``` + mvn clean install -DskipTests + ``` + - If IntelliJ uses a different Maven version, use the Maven window in IntelliJ: under `Lifecycle`, click `clean` and then `install`. + +### Running Tests -* braces must be used with `if`, `else`, `for`, `do` and `while` statements, even when the body is - empty or contains only a single statement. -* XML files: indent with two spaces and wrap to respect the column limit of 100 characters. +#### Unit Tests + +```bash +mvn clean install -DskipTests +mvn test +``` -## Coding style -- production code +#### Integration Tests + +1. Install Cassandra Cluster Manager (CCM) following its [README](https://github.com/apache/cassandra-ccm). +2. **MacOS only**, for CCM and Simulacron-based tests, enable loopback aliases: + ```shell + for i in {2..255}; do sudo ifconfig lo0 alias 127.0.0.$i up; done + ``` + Note: This may slow down networking. To remove the aliases after testing: + ```shell + for i in {2..255}; do sudo ifconfig lo0 -alias 127.0.0.$i up; done + ``` +3. **MacOS Apple Silicon only**, for some Cassandra versions, you might need to work around the JNA 5.6.0 version, which is incompatible to Apple Silicon. + ```shell + mvn dependency:get -Dartifact=net.java.dev.jna:jna:5.10.0 + cp ~/.m2/repository/net/java/dev/jna/jna/5.10.0/jna-5.10.0.jar ~/.ccm/repository/4.0.19/lib/jna-5.6.0.jar + ``` +4. Run integration tests: + ``` + mvn clean verify + ``` + To target a specific Cassandra version or distribution: + ``` + mvn verify -Dccm.version=3.11.0 + mvn verify -Dccm.distribution=dse -Dccm.version=6.8.0 + ``` + +--- + +## Coding Guidelines + +### General Do not use static imports. They make things harder to understand when you look at the code someplace where you don't have IDE support, like Github's code view. @@ -30,6 +197,19 @@ understood cases (like `i` for a loop index). Keep source files short. Short files are easy to understand and test. The average should probably be around 200-300 lines. +### Code Formatting and License Headers + +- We follow the [Google Java Style Guide](https://google.github.io/styleguide/javaguide.html). See [google-java-format](https://github.com/google/google-java-format) for IDE plugins. +- To format code: + ``` + # Java files + mvn fmt:format + # XML files + mvn xml-format:xml-format + # License headers + mvn license:format + ``` + ### Javadoc All types in "API" packages must be documented. For "internal" packages, documentation is optional, @@ -39,6 +219,7 @@ where the component fits in the architecture, and anything else that you feel is You don't need to document every parameter or return type, or even every method. Don't document something if it is completely obvious, we don't want to end up with this: + ```java /** * Returns the name. @@ -67,7 +248,7 @@ in the documented item's signature. Builder withLimit(int limit) { ``` -### Logs +### Logging We use SLF4J; loggers are declared like this: @@ -80,7 +261,7 @@ Logs are intended for two personae: * Ops who manage the application in production. * Developers (maybe you) who debug a particular issue. -The first 3 log levels are for ops: +#### Log levels * `ERROR`: something that renders the driver -- or a part of it -- completely unusable. An action is required to fix it: bouncing the client, applying a patch, etc. @@ -109,16 +290,8 @@ perspective (think about debugging an issue remotely, and all you have are the l Note that `DEBUG` and `TRACE` can coexist within the same component, for example the LBP initializing is a one-time event, but returning a query plan is a per-request event. -Logs statements start with a prefix that identifies its origin, for example: - -* for components that are unique to the cluster instance, just the cluster name: `[c0]`. -* for sessions, the cluster name + a generated unique identifier: `[c0|s0]`. -* for channel pools, the session identifier + the address of the node: `[c0|s0|/127.0.0.2:9042]`. -* for channels, the identifier of the owner (session or control connection) + the Netty identifier, - which indicates the local and remote ports: - `[c0|s0|id: 0xf9ef0b15, L:/127.0.0.1:51482 - R:/127.0.0.1:9042]`. -* for request handlers, the session identifier, a unique identifier, and the index of the - speculative execution: `[c0|s0|1077199500|0]`. +**Log prefix** shows origin, e.g.: +`[s0|90232530|0]` (session name | hash code of the CqlRequestHandler instance | number of request attempts) Tests run with the configuration defined in `src/test/resources/logback-test.xml`. The default level for driver classes is `WARN`, but you can override it with a system property: `-DdriverLevel=DEBUG`. @@ -128,43 +301,29 @@ line. When you add or review new code, take a moment to run the tests in `DEBUG` mode and check if the output looks good. -### No stream API - -Please don't use `java.util.stream` in the driver codebase. Streams were designed for *data -processing*, not to make your collection traversals "functional". +### Don't abuse the stream API -Here's an example from the driver codebase (`ChannelSet`): +The `java.util.stream` API is often used (abused?) as a "functional API for collections": ```java -DriverChannel[] snapshot = this.channels; -DriverChannel best = null; -int bestScore = 0; -for (DriverChannel channel : snapshot) { - int score = channel.availableIds(); - if (score > bestScore) { - bestScore = score; - best = channel; - } -} -return best; +List sizes = words.stream().map(String::length).collect(Collectors.toList()); ``` -And here's a terrible way to rewrite it using streams: +The perceived advantages of this approach over traditional for-loops are debatable: -```java -// Don't do this: -DriverChannel best = - Stream.of(snapshot) - .reduce((a, b) -> a.availableIds() > b.availableIds() ? a : b) - .get(); -``` +* readability: this is highly subjective. But consider the following: + * everyone can read for-loops, whether they are familiar with the Stream API or not. The opposite + is not true. + * the stream API does not spell out all the details: what kind of list does `Collectors.toList()` + return? Is it pre-sized? Mutable? Thread-safe? + * the stream API looks pretty on simple examples, but things can get ugly fast. Try rewriting + `NetworkTopologyReplicationStrategy` with streams. +* concision: this is irrelevant. When we look at code we care about maintainability, not how many + keystrokes the author saved. The for-loop version of the above example is just 5 lines long, and + your brain doesn't take longer to parse it. -The stream version is not easier to read, and will probably be slower (creating intermediary objects -vs. an array iteration, compounded by the fact that this particular array typically has a low -cardinality). - -The driver never does the kind of processing that the stream API is intended for; the only large -collections we manipulate are result sets, and these get passed on to the client directly. +The bottom line: don't try to "be functional" at all cost. Plain old for-loops are often just as +simple. ### Never assume a specific format for `toString()` @@ -203,7 +362,7 @@ Please annotate any new class or interface with the appropriate annotations: `@N the types from `edu.umd.cs.findbugs.annotations`, there are homonyms in the classpath. -## Coding style -- test code +## Coding Guidelines for Tests Static imports are permitted in a couple of places: * All AssertJ methods, e.g.: @@ -216,6 +375,10 @@ Static imports are permitted in a couple of places: when(codecRegistry.codecFor(DataTypes.INT)).thenReturn(codec); verify(codec).decodePrimitive(any(ByteBuffer.class), eq(ProtocolVersion.DEFAULT)); ``` +* All Awaitility methods, e.g.: + ```java + await().until(() -> somethingBecomesTrue()); + ``` Test methods names use lower snake case, generally start with `should`, and clearly indicate the purpose of the test, for example: `should_fail_if_key_already_exists`. If you have trouble coming @@ -258,9 +421,11 @@ process, which can be either one of: For an example of a CCM-based test, see `PlainTextAuthProviderIT`. +#### Categories + Integration tests are divided into three categories: -#### Parallelizable tests +##### Parallelizable tests These tests can be run in parallel, to speed up the build. They either use: * dedicated Simulacron instances. These are lightweight, and Simulacron will manage the ports to @@ -268,7 +433,9 @@ These tests can be run in parallel, to speed up the build. They either use: * a shared, one-node CCM cluster. Each test works in its own keyspace. The build runs them with a configurable degree of parallelism (currently 8). The shared CCM cluster -is initialized the first time it's used, and stopped before moving on to serial tests. +is initialized the first time it's used, and stopped before moving on to serial tests. Note that we +run with `parallel=classes`, which means methods within the same class never run concurrent to each +other. To make an integration test parallelizable, annotate it with `@Category(ParallelizableTests.class)`. If you use CCM, it **must** be with `CcmRule`. @@ -276,7 +443,7 @@ If you use CCM, it **must** be with `CcmRule`. For an example of a Simulacron-based parallelizable test, see `NodeTargetingIT`. For a CCM-based test, see `DirectCompressionIT`. -#### Serial tests +##### Serial tests These tests cannot run in parallel, in general because they require CCM clusters of different sizes, or with a specific configuration (we never run more than one CCM cluster simultaneously: it would be @@ -293,7 +460,7 @@ Note: if multiple serial tests have a common "base" class, do not pull up `Custo child class must have its own instance. Otherwise they share the same CCM instance, and the first one destroys it on teardown. See `TokenITBase` for how to organize code in those cases. -#### Isolated tests +##### Isolated tests Not only can those tests not run in parallel, they also require specific environment tweaks, typically system properties that need to be set before initialization. @@ -305,139 +472,52 @@ To isolate an integration test, annotate it with `@Category(IsolatedTests.class) For an example, see `HeapCompressionIT`. +#### About test rules -## Running the tests +Do not mix `CcmRule` and `SimulacronRule` in the same test. It makes things harder to follow, and +can be inefficient (if the `SimulacronRule` is method-level, it will create a Simulacron cluster for +every test method, even those that only need CCM). -### Unit tests - - mvn clean test - -This currently takes about 30 seconds. The goal is to keep it within a couple of minutes (it runs -for each commit if you enable the pre-commit hook -- see below). - -### Integration tests - - mvn clean verify +Use the `@BackendRequirement` annotation to restrict the backend type and version. +Specify the Cassandra Distribution, CASSANDRA/DSE/HCD, and the version requirement. +For example, `@BackendRequirement(type = BackendType.CASSANDRA, minInclusive = "2.2")` -This currently takes about 9 minutes. We don't have a hard limit, but ideally it should stay within -30 minutes to 1 hour. +##### Class-level rules -You can skip test categories individually with `-DskipParallelizableITs`, `-DskipSerialITs` and -`-DskipIsolatedITs`. +Rules annotated with `@ClassRule` wrap the whole test class, and are reused across methods. Try to +use this as much as possible, as it's more efficient. The fields need to be static; also make them +final and use constant naming conventions, like `CCM_RULE`. -### Configuring MacOS for Simulacron +When you use a server rule (`CcmRule` or `SimulacronRule`) and a `SessionRule` at the same level, +wrap them into a rule chain to ensure proper initialization order: -Simulacron (used in integration tests) relies on loopback aliases to simulate multiple nodes. On -Linux or Windows, you shouldn't have anything to do. On MacOS, run this script: - -``` -#!/bin/bash -for sub in {0..4}; do - echo "Opening for 127.0.$sub" - for i in {0..255}; do sudo ifconfig lo0 alias 127.0.$sub.$i up; done -done -``` - -Note that this is known to cause temporary increased CPU usage in OS X initially while mDNSResponder -acclimates itself to the presence of added IP addresses. This lasts several minutes. Also, this does -not survive reboots. - - -## License headers - -The build will fail if some license headers are missing. To update all files from the command line, -run: - -``` -mvn license:format -``` - -## Pre-commit hook (highly recommended) - -Ensure `pre-commit.sh` is executable, then run: +```java +private static final CcmRule CCM_RULE = CcmRule.getInstance(); +private static final SessionRule SESSION_RULE = SessionRule.builder(CCM_RULE).build(); +@ClassRule +public static final TestRule CHAIN = RuleChain.outerRule(CCM_RULE).around(SESSION_RULE); ``` -ln -s ../../pre-commit.sh .git/hooks/pre-commit -``` - -This will only allow commits if the tests pass. It is also a good reminder to keep the test suite -short. - -Note: the tests run on the current state of the working directory. I tried to add a `git stash` in -the script to only test what's actually being committed, but I couldn't get it to run reliably -(it's still in there but commented). Keep this in mind when you commit, and don't forget to re-add -the changes if the first attempt failed and you fixed the tests. - -## Commits - -Keep your changes **focused**. Each commit should have a single, clear purpose expressed in its -message. - -Resist the urge to "fix" cosmetic issues (add/remove blank lines, move methods, etc.) in existing -code. This adds cognitive load for reviewers, who have to figure out which changes are relevant to -the actual issue. If you see legitimate issues, like typos, address them in a separate commit (it's -fine to group multiple typo fixes in a single commit). -Isolate trivial refactorings into separate commits. For example, a method rename that affects dozens -of call sites can be reviewed in a few seconds, but if it's part of a larger diff it gets mixed up -with more complex changes (that might affect the same lines), and reviewers have to check every -line. - -Commit message subjects start with a capital letter, use the imperative form and do **not** end -with a period: - -* correct: "Add test for CQL request handler" -* incorrect: "~~Added test for CQL request handler~~" -* incorrect: "~~New test for CQL request handler~~" - -Avoid catch-all messages like "Minor cleanup", "Various fixes", etc. They don't provide any useful -information to reviewers, and might be a sign that your commit contains unrelated changes. - -We don't enforce a particular subject line length limit, but try to keep it short. +##### Method-level rules -You can add more details after the subject line, separated by a blank line. The following pattern -(inspired by [Netty](http://netty.io/wiki/writing-a-commit-message.html)) is not mandatory, but -welcome for complex changes: +Rules annotated with `@Rule` wrap each test method. Use lower-camel case for field names: -``` -One line description of your change - -Motivation: - -Explain here the context, and why you're making that change. -What is the problem you're trying to solve. - -Modifications: - -Describe the modifications you've done. - -Result: +```java +private CcmRule ccmRule = CcmRule.getInstance(); +private SessionRule sessionRule = SessionRule.builder(ccmRule).build(); -After your change, what will change. +@ClassRule +public TestRule chain = RuleChain.outerRule(ccmRule).around(sessionRule); ``` -## Pull requests - -Like commits, pull requests should be focused on a single, clearly stated goal. +Only use this for: -Don't base a pull request onto another one, it's too complicated to follow two branches that evolve -at the same time. If a ticket depends on another, wait for the first one to be merged. - -If you have to address feedback, avoid rewriting the history (e.g. squashing or amending commits): -this makes the reviewers' job harder, because they have to re-read the full diff and figure out -where your new changes are. Instead, push a new commit on top of the existing history; it will be -squashed later when the PR gets merged. If the history is complex, it's a good idea to indicate in -the message where the changes should be squashed: - -``` -* 20c88f4 - Address feedback (to squash with "Add metadata parsing logic") (36 minutes ago) -* 7044739 - Fix various typos in Javadocs (2 days ago) -* 574dd08 - Add metadata parsing logic (2 days ago) -``` +* CCM tests that use `@BackendRequirement` restrictions at the method level + (ex: `BatchStatementIT`). +* tests where you *really* need to restart from a clean state for every method. -(Note that the message refers to the other commit's subject line, not the SHA-1. This way it's still -relevant if there are intermediary rebases.) +##### Mixed -If you need new stuff from the base branch, it's fine to rebase and force-push, as long as you don't -rewrite the history. Just give a heads up to the reviewers beforehand. Don't push a merge commit to -a pull request. +It's also possible to use a `@ClassRule` for CCM / Simulacron, and a `@Rule` for the session rule. +In that case, you don't need to use a rule chain. diff --git a/Jenkinsfile-asf b/Jenkinsfile-asf new file mode 100644 index 00000000000..4b5041903c1 --- /dev/null +++ b/Jenkinsfile-asf @@ -0,0 +1,81 @@ +#!groovy + +/* + * 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. + */ + +pipeline { + agent { + label 'cassandra-small' + } + + triggers { + // schedules only run against release branches (i.e. 3.x, 4.x, 4.5.x, etc.) + cron(branchPatternCron().matcher(env.BRANCH_NAME).matches() ? '@weekly' : '') + } + + stages { + stage('Matrix') { + matrix { + axes { + axis { + name 'TEST_JAVA_VERSION' + values 'openjdk@1.8.0-292', 'openjdk@1.11.0-9', 'openjdk@1.17.0', 'openjdk@1.21.0' + } + axis { + name 'SERVER_VERSION' + values '3.11', + '4.0', + '4.1', + '5.0' + } + } + stages { + stage('Tests') { + agent { + label 'cassandra-medium' + } + steps { + script { + executeTests() + junit testResults: '**/target/surefire-reports/TEST-*.xml', allowEmptyResults: true + junit testResults: '**/target/failsafe-reports/TEST-*.xml', allowEmptyResults: true + } + } + } + } + } + } + } +} + +def executeTests() { + def testJavaMajorVersion = (TEST_JAVA_VERSION =~ /@(?:1\.)?(\d+)/)[0][1] + sh """ + container_id=\$(docker run -td -e TEST_JAVA_VERSION=${TEST_JAVA_VERSION} -e SERVER_VERSION=${SERVER_VERSION} -e TEST_JAVA_MAJOR_VERSION=${testJavaMajorVersion} -v \$(pwd):/home/docker/cassandra-java-driver apache.jfrog.io/cassan-docker/apache/cassandra-java-driver-testing-ubuntu2204 'sleep 2h') + docker exec --user root \$container_id bash -c \"sudo bash /home/docker/cassandra-java-driver/ci/create-user.sh docker \$(id -u) \$(id -g) /home/docker/cassandra-java-driver\" + docker exec --user docker \$container_id './cassandra-java-driver/ci/run-tests.sh' + ( nohup docker stop \$container_id >/dev/null 2>/dev/null & ) + """ +} + +// branch pattern for cron +// should match 3.x, 4.x, 4.5.x, etc +def branchPatternCron() { + ~'((\\d+(\\.[\\dx]+)+))' +} diff --git a/Jenkinsfile-datastax b/Jenkinsfile-datastax new file mode 100644 index 00000000000..602f33101ca --- /dev/null +++ b/Jenkinsfile-datastax @@ -0,0 +1,639 @@ +#!groovy +/* + * 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. + */ + +def initializeEnvironment() { + env.DRIVER_DISPLAY_NAME = 'Java Driver for Apache CassandraⓇ' + env.DRIVER_METRIC_TYPE = 'oss' + + env.GIT_SHA = "${env.GIT_COMMIT.take(7)}" + env.GITHUB_PROJECT_URL = "https://${GIT_URL.replaceFirst(/(git@|http:\/\/|https:\/\/)/, '').replace(':', '/').replace('.git', '')}" + env.GITHUB_BRANCH_URL = "${GITHUB_PROJECT_URL}/tree/${env.BRANCH_NAME}" + env.GITHUB_COMMIT_URL = "${GITHUB_PROJECT_URL}/commit/${env.GIT_COMMIT}" + + env.MAVEN_HOME = "${env.HOME}/.mvn/apache-maven-3.8.8" + env.PATH = "${env.MAVEN_HOME}/bin:${env.PATH}" + + /* + * As of JAVA-3042 JAVA_HOME is always set to JDK8 and this is currently necessary for mvn compile and DSE Search/Graph. + * To facilitate testing with JDK11/17 we feed the appropriate JAVA_HOME into the maven build via commandline. + * + * Maven command-line flags: + * - -DtestJavaHome=/path/to/java/home: overrides JAVA_HOME for surefire/failsafe tests, defaults to environment JAVA_HOME. + * - -Ptest-jdk-N: enables profile for running tests with a specific JDK version (substitute N for 8/11/17). + * + * Note test-jdk-N is also automatically loaded based off JAVA_HOME SDK version so testing with an older SDK is not supported. + * + * Environment variables: + * - JAVA_HOME: Path to JDK used for mvn (all steps except surefire/failsafe), Cassandra, DSE. + * - JAVA8_HOME: Path to JDK8 used for Cassandra/DSE if ccm determines JAVA_HOME is not compatible with the chosen backend. + * - TEST_JAVA_HOME: PATH to JDK used for surefire/failsafe testing. + * - TEST_JAVA_VERSION: TEST_JAVA_HOME SDK version number [8/11/17], used to configure test-jdk-N profile in maven (see above) + */ + + env.JAVA_HOME = sh(label: 'Get JAVA_HOME',script: '''#!/bin/bash -le + . ${JABBA_SHELL} + jabba which ${JABBA_VERSION}''', returnStdout: true).trim() + env.JAVA8_HOME = sh(label: 'Get JAVA8_HOME',script: '''#!/bin/bash -le + . ${JABBA_SHELL} + jabba which 1.8''', returnStdout: true).trim() + + sh label: 'Download Apache CassandraⓇ, DataStax Enterprise or DataStax HCD ',script: '''#!/bin/bash -le + . ${JABBA_SHELL} + jabba use 1.8 + . ${CCM_ENVIRONMENT_SHELL} ${SERVER_VERSION} + ''' + + if (env.SERVER_VERSION.split('-')[0] == 'dse') { + env.DSE_FIXED_VERSION = env.SERVER_VERSION.split('-')[1] + sh label: 'Update environment for DataStax Enterprise', script: '''#!/bin/bash -le + cat >> ${HOME}/environment.txt << ENVIRONMENT_EOF +CCM_CASSANDRA_VERSION=${DSE_FIXED_VERSION} # maintain for backwards compatibility +CCM_VERSION=${DSE_FIXED_VERSION} +CCM_SERVER_TYPE=dse +DSE_VERSION=${DSE_FIXED_VERSION} +CCM_BRANCH=${DSE_FIXED_VERSION} +DSE_BRANCH=${DSE_FIXED_VERSION} +ENVIRONMENT_EOF + ''' + } + + if (env.SERVER_VERSION.split('-')[0] == 'hcd') { + env.HCD_FIXED_VERSION = env.SERVER_VERSION.split('-')[1] + sh label: 'Update environment for DataStax HCD', script: '''#!/bin/bash -le + cat >> ${HOME}/environment.txt << ENVIRONMENT_EOF +CCM_CASSANDRA_VERSION=${HCD_FIXED_VERSION} # maintain for backwards compatibility +CCM_VERSION=${HCD_FIXED_VERSION} +CCM_SERVER_TYPE=hcd +HCD_VERSION=${HCD_FIXED_VERSION} +CCM_BRANCH=${HCD_FIXED_VERSION} +HCD_BRANCH=${HCD_FIXED_VERSION} +ENVIRONMENT_EOF + ''' + } + + sh label: 'Display Java and environment information',script: '''#!/bin/bash -le + # Load CCM environment variables + set -o allexport + . ${HOME}/environment.txt + set +o allexport + + . ${JABBA_SHELL} + jabba use 1.8 + + java -version + mvn -v + printenv | sort + ''' +} + +def buildDriver(jabbaVersion) { + def buildDriverScript = '''#!/bin/bash -le + + . ${JABBA_SHELL} + jabba use '''+jabbaVersion+''' + + echo "Building with Java version '''+jabbaVersion+'''" + + mvn -B -V install -DskipTests -Dmaven.javadoc.skip=true + ''' + sh label: 'Build driver', script: buildDriverScript +} + +def executeTests() { + def testJavaHome = sh(label: 'Get TEST_JAVA_HOME',script: '''#!/bin/bash -le + . ${JABBA_SHELL} + jabba which ${JABBA_VERSION}''', returnStdout: true).trim() + def testJavaVersion = (JABBA_VERSION =~ /.*\.(\d+)/)[0][1] + + def executeTestScript = '''#!/bin/bash -le + # Load CCM environment variables + set -o allexport + . ${HOME}/environment.txt + set +o allexport + + . ${JABBA_SHELL} + jabba use 1.8 + + if [ "${JABBA_VERSION}" != "1.8" ]; then + SKIP_JAVADOCS=true + else + SKIP_JAVADOCS=false + fi + + INTEGRATION_TESTS_FILTER_ARGUMENT="" + if [ ! -z "${INTEGRATION_TESTS_FILTER}" ]; then + INTEGRATION_TESTS_FILTER_ARGUMENT="-Dit.test=${INTEGRATION_TESTS_FILTER}" + fi + printenv | sort + + mvn -B -V ${INTEGRATION_TESTS_FILTER_ARGUMENT} -T 1 verify \ + -Ptest-jdk-'''+testJavaVersion+''' \ + -DtestJavaHome='''+testJavaHome+''' \ + -DfailIfNoTests=false \ + -Dmaven.test.failure.ignore=true \ + -Dmaven.javadoc.skip=${SKIP_JAVADOCS} \ + -Dccm.version=${CCM_CASSANDRA_VERSION} \ + -Dccm.distribution=${CCM_SERVER_TYPE:cassandra} \ + -Dproxy.path=${HOME}/proxy \ + ${SERIAL_ITS_ARGUMENT} \ + ${ISOLATED_ITS_ARGUMENT} \ + ${PARALLELIZABLE_ITS_ARGUMENT} + ''' + echo "Invoking Maven with parameters test-jdk-${testJavaVersion} and testJavaHome = ${testJavaHome}" + sh label: 'Execute tests', script: executeTestScript +} + +def executeCodeCoverage() { + jacoco( + execPattern: '**/target/jacoco.exec', + classPattern: '**/classes', + sourcePattern: '**/src/main/java' + ) +} + +def notifySlack(status = 'started') { + // Notify Slack channel for every build except adhoc executions + if (params.ADHOC_BUILD_TYPE != 'BUILD-AND-EXECUTE-TESTS') { + // Set the global pipeline scoped environment (this is above each matrix) + env.BUILD_STATED_SLACK_NOTIFIED = 'true' + + def buildType = 'Commit' + if (params.CI_SCHEDULE != 'DO-NOT-CHANGE-THIS-SELECTION') { + buildType = "${params.CI_SCHEDULE.toLowerCase().capitalize()}" + } + + def color = 'good' // Green + if (status.equalsIgnoreCase('aborted')) { + color = '808080' // Grey + } else if (status.equalsIgnoreCase('unstable')) { + color = 'warning' // Orange + } else if (status.equalsIgnoreCase('failed')) { + color = 'danger' // Red + } + + def message = """Build ${status} for ${env.DRIVER_DISPLAY_NAME} [${buildType}] +<${env.GITHUB_BRANCH_URL}|${env.BRANCH_NAME}> - <${env.RUN_DISPLAY_URL}|#${env.BUILD_NUMBER}> - <${env.GITHUB_COMMIT_URL}|${env.GIT_SHA}>""" + if (!status.equalsIgnoreCase('Started')) { + message += """ +${status} after ${currentBuild.durationString - ' and counting'}""" + } + + slackSend color: "${color}", + channel: "#java-driver-dev-bots", + message: "${message}" + } +} + +def describePerCommitStage() { + script { + currentBuild.displayName = "Per-Commit build" + currentBuild.description = 'Per-Commit build and testing of development Apache CassandraⓇ and current DataStax Enterprise against Oracle JDK 8' + } +} + +def describeAdhocAndScheduledTestingStage() { + script { + if (params.CI_SCHEDULE == 'DO-NOT-CHANGE-THIS-SELECTION') { + // Ad-hoc build + currentBuild.displayName = "Adhoc testing" + currentBuild.description = "Testing ${params.ADHOC_BUILD_AND_EXECUTE_TESTS_SERVER_VERSION} against JDK version ${params.ADHOC_BUILD_AND_EXECUTE_TESTS_JABBA_VERSION}" + } else { + // Scheduled build + currentBuild.displayName = "${params.CI_SCHEDULE.toLowerCase().replaceAll('_', ' ').capitalize()} schedule" + currentBuild.description = "Testing server versions [${params.CI_SCHEDULE_SERVER_VERSIONS}] against JDK version ${params.CI_SCHEDULE_JABBA_VERSION}" + } + } +} + +// branch pattern for cron +// should match 3.x, 4.x, 4.5.x, etc +def branchPatternCron() { + ~"((\\d+(\\.[\\dx]+)+))" +} + +pipeline { + agent none + + // Global pipeline timeout + options { + timeout(time: 10, unit: 'HOURS') + buildDiscarder(logRotator(artifactNumToKeepStr: '10', // Keep only the last 10 artifacts + numToKeepStr: '50')) // Keep only the last 50 build records + } + + parameters { + choice( + name: 'ADHOC_BUILD_TYPE', + choices: ['BUILD', 'BUILD-AND-EXECUTE-TESTS'], + description: '''

Perform a adhoc build operation

+ + + + + + + + + + + + + + + +
ChoiceDescription
BUILDPerforms a Per-Commit build
BUILD-AND-EXECUTE-TESTSPerforms a build and executes the integration and unit tests
''') + choice( + name: 'ADHOC_BUILD_AND_EXECUTE_TESTS_SERVER_VERSION', + choices: ['4.0', // Previous Apache CassandraⓇ + '4.1', // Previous Apache CassandraⓇ + '5.0', // Current Apache CassandraⓇ + 'dse-4.8.16', // Previous EOSL DataStax Enterprise + 'dse-5.0.15', // Long Term Support DataStax Enterprise + 'dse-5.1.35', // Legacy DataStax Enterprise + 'dse-6.0.18', // Previous DataStax Enterprise + 'dse-6.7.17', // Previous DataStax Enterprise + 'dse-6.8.30', // Current DataStax Enterprise + 'dse-6.9.0', // Current DataStax Enterprise + 'hcd-1.0.0', // Current DataStax HCD + 'ALL'], + description: '''Apache Cassandra® and DataStax Enterprise server version to use for adhoc BUILD-AND-EXECUTE-TESTS builds + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ChoiceDescription
4.0Apache Cassandra® v4.0.x
4.1Apache Cassandra® v4.1.x
5.0Apache Cassandra® v5.0.x
dse-4.8.16DataStax Enterprise v4.8.x (END OF SERVICE LIFE)
dse-5.0.15DataStax Enterprise v5.0.x (Long Term Support)
dse-5.1.35DataStax Enterprise v5.1.x
dse-6.0.18DataStax Enterprise v6.0.x
dse-6.7.17DataStax Enterprise v6.7.x
dse-6.8.30DataStax Enterprise v6.8.x
dse-6.9.0DataStax Enterprise v6.9.x
hcd-1.0.0DataStax HCD v1.0.x
''') + choice( + name: 'ADHOC_BUILD_AND_EXECUTE_TESTS_JABBA_VERSION', + choices: [ + '1.8', // Oracle JDK version 1.8 (current default) + 'openjdk@1.11', // OpenJDK version 11 + 'openjdk@1.17', // OpenJDK version 17 + 'openjdk@1.21' // OpenJDK version 21 + ], + description: '''JDK version to use for TESTING when running adhoc BUILD-AND-EXECUTE-TESTS builds. All builds will use JDK8 for building the driver + + + + + + + + + + + + + + + + + + + + + + + +
ChoiceDescription
1.8Oracle JDK version 1.8 (Used for compiling regardless of choice)
openjdk@1.11OpenJDK version 11
openjdk@1.17OpenJDK version 17
openjdk@1.21OpenJDK version 21
''') + booleanParam( + name: 'SKIP_SERIAL_ITS', + defaultValue: false, + description: 'Flag to determine if serial integration tests should be skipped') + booleanParam( + name: 'SKIP_ISOLATED_ITS', + defaultValue: false, + description: 'Flag to determine if isolated integration tests should be skipped') + booleanParam( + name: 'SKIP_PARALLELIZABLE_ITS', + defaultValue: false, + description: 'Flag to determine if parallel integration tests should be skipped') + string( + name: 'INTEGRATION_TESTS_FILTER', + defaultValue: '', + description: '''

Run only the tests whose name match patterns

+ See Maven Failsafe Plugin for more information on filtering integration tests''') + choice( + name: 'CI_SCHEDULE', + choices: ['DO-NOT-CHANGE-THIS-SELECTION', 'WEEKNIGHTS', 'WEEKENDS', 'MONTHLY'], + description: 'CI testing schedule to execute periodically scheduled builds and tests of the driver (DO NOT CHANGE THIS SELECTION)') + string( + name: 'CI_SCHEDULE_SERVER_VERSIONS', + defaultValue: 'DO-NOT-CHANGE-THIS-SELECTION', + description: 'CI testing server version(s) to utilize for scheduled test runs of the driver (DO NOT CHANGE THIS SELECTION)') + string( + name: 'CI_SCHEDULE_JABBA_VERSION', + defaultValue: 'DO-NOT-CHANGE-THIS-SELECTION', + description: 'CI testing JDK version(s) to utilize for scheduled test runs of the driver (DO NOT CHANGE THIS SELECTION)') + } + + triggers { + // schedules only run against release branches (i.e. 3.x, 4.x, 4.5.x, etc.) + parameterizedCron(branchPatternCron().matcher(env.BRANCH_NAME).matches() ? """ + # Every weekend (Saturday, Sunday) around 2:00 AM + H 2 * * 0 %CI_SCHEDULE=WEEKENDS;CI_SCHEDULE_SERVER_VERSIONS=4.0 4.1 5.0 dse-4.8.16 dse-5.0.15 dse-5.1.35 dse-6.0.18 dse-6.7.17;CI_SCHEDULE_JABBA_VERSION=1.8 + # Every weeknight (Monday - Friday) around 12:00 PM noon + H 12 * * 1-5 %CI_SCHEDULE=WEEKNIGHTS;CI_SCHEDULE_SERVER_VERSIONS=4.1 5.0 dse-6.8.30 dse-6.9.0 hcd-1.0.0;CI_SCHEDULE_JABBA_VERSION=openjdk@1.11 + H 12 * * 1-5 %CI_SCHEDULE=WEEKNIGHTS;CI_SCHEDULE_SERVER_VERSIONS=4.1 5.0 dse-6.8.30 dse-6.9.0 hcd-1.0.0;CI_SCHEDULE_JABBA_VERSION=openjdk@1.17 + """ : "") + } + + environment { + OS_VERSION = 'ubuntu/focal64/java-driver' + JABBA_SHELL = '/usr/lib/jabba/jabba.sh' + CCM_ENVIRONMENT_SHELL = '/usr/local/bin/ccm_environment.sh' + SERIAL_ITS_ARGUMENT = "-DskipSerialITs=${params.SKIP_SERIAL_ITS}" + ISOLATED_ITS_ARGUMENT = "-DskipIsolatedITs=${params.SKIP_ISOLATED_ITS}" + PARALLELIZABLE_ITS_ARGUMENT = "-DskipParallelizableITs=${params.SKIP_PARALLELIZABLE_ITS}" + INTEGRATION_TESTS_FILTER = "${params.INTEGRATION_TESTS_FILTER}" + } + + stages { + stage ('Per-Commit') { + options { + timeout(time: 2, unit: 'HOURS') + } + when { + beforeAgent true + allOf { + expression { params.ADHOC_BUILD_TYPE == 'BUILD' } + expression { params.CI_SCHEDULE == 'DO-NOT-CHANGE-THIS-SELECTION' } + expression { params.CI_SCHEDULE_SERVER_VERSIONS == 'DO-NOT-CHANGE-THIS-SELECTION' } + expression { params.CI_SCHEDULE_JABBA_VERSION == 'DO-NOT-CHANGE-THIS-SELECTION' } + not { buildingTag() } + } + } + + matrix { + axes { + axis { + name 'SERVER_VERSION' + values '4.0', // Previous Apache CassandraⓇ + '5.0', // Current Apache CassandraⓇ + 'dse-6.8.30', // Current DataStax Enterprise + 'dse-6.9.0', // Current DataStax Enterprise + 'hcd-1.0.0' // Current DataStax HCD + } + axis { + name 'JABBA_VERSION' + values '1.8', // jdk8 + 'openjdk@1.11', // jdk11 + 'openjdk@1.17', // jdk17 + 'openjdk@1.21' // jdk21 + } + } + + agent { + label "${OS_VERSION}" + } + + stages { + stage('Initialize-Environment') { + steps { + initializeEnvironment() + script { + if (env.BUILD_STATED_SLACK_NOTIFIED != 'true') { + notifySlack() + } + } + } + } + stage('Describe-Build') { + steps { + describePerCommitStage() + } + } + stage('Build-Driver') { + steps { + buildDriver('1.8') + } + } + stage('Execute-Tests') { + steps { + catchError { + // Use the matrix JDK for testing + executeTests() + } + } + post { + always { + /* + * Empty results are possible + * + * - Build failures during mvn verify may exist so report may not be available + */ + junit testResults: '**/target/surefire-reports/TEST-*.xml', allowEmptyResults: true + junit testResults: '**/target/failsafe-reports/TEST-*.xml', allowEmptyResults: true + } + } + } + stage('Execute-Code-Coverage') { + // Ensure the code coverage is run only once per-commit + when { environment name: 'SERVER_VERSION', value: '4.0' } + steps { + executeCodeCoverage() + } + } + } + } + post { + aborted { + notifySlack('aborted') + } + success { + notifySlack('completed') + } + unstable { + notifySlack('unstable') + } + failure { + notifySlack('FAILED') + } + } + } + + stage('Adhoc-And-Scheduled-Testing') { + when { + beforeAgent true + allOf { + expression { (params.ADHOC_BUILD_TYPE == 'BUILD' && params.CI_SCHEDULE != 'DO-NOT-CHANGE-THIS-SELECTION') || + params.ADHOC_BUILD_TYPE == 'BUILD-AND-EXECUTE-TESTS' } + not { buildingTag() } + anyOf { + expression { params.ADHOC_BUILD_TYPE == 'BUILD-AND-EXECUTE-TESTS' } + allOf { + expression { params.ADHOC_BUILD_TYPE == 'BUILD' } + expression { params.CI_SCHEDULE != 'DO-NOT-CHANGE-THIS-SELECTION' } + expression { params.CI_SCHEDULE_SERVER_VERSIONS != 'DO-NOT-CHANGE-THIS-SELECTION' } + } + } + } + } + + environment { + SERVER_VERSIONS = "${params.CI_SCHEDULE_SERVER_VERSIONS == 'DO-NOT-CHANGE-THIS-SELECTION' ? params.ADHOC_BUILD_AND_EXECUTE_TESTS_SERVER_VERSION : params.CI_SCHEDULE_SERVER_VERSIONS}" + JABBA_VERSION = "${params.CI_SCHEDULE_JABBA_VERSION == 'DO-NOT-CHANGE-THIS-SELECTION' ? params.ADHOC_BUILD_AND_EXECUTE_TESTS_JABBA_VERSION : params.CI_SCHEDULE_JABBA_VERSION}" + } + + matrix { + axes { + axis { + name 'SERVER_VERSION' + values '4.0', // Previous Apache CassandraⓇ + '4.1', // Previous Apache CassandraⓇ + '5.0', // Current Apache CassandraⓇ + 'dse-4.8.16', // Previous EOSL DataStax Enterprise + 'dse-5.0.15', // Last EOSL DataStax Enterprise + 'dse-5.1.35', // Legacy DataStax Enterprise + 'dse-6.0.18', // Previous DataStax Enterprise + 'dse-6.7.17', // Previous DataStax Enterprise + 'dse-6.8.30', // Current DataStax Enterprise + 'dse-6.9.0', // Current DataStax Enterprise + 'hcd-1.0.0' // Current DataStax HCD + } + } + when { + beforeAgent true + allOf { + expression { return env.SERVER_VERSIONS.split(' ').any { it =~ /(ALL|${env.SERVER_VERSION})/ } } + } + } + agent { + label "${env.OS_VERSION}" + } + + stages { + stage('Initialize-Environment') { + steps { + initializeEnvironment() + script { + if (env.BUILD_STATED_SLACK_NOTIFIED != 'true') { + notifySlack() + } + } + } + } + stage('Describe-Build') { + steps { + describeAdhocAndScheduledTestingStage() + } + } + stage('Build-Driver') { + steps { + buildDriver('1.8') + } + } + stage('Execute-Tests') { + steps { + catchError { + // Use the matrix JDK for testing + executeTests() + } + } + post { + always { + /* + * Empty results are possible + * + * - Build failures during mvn verify may exist so report may not be available + * - With boolean parameters to skip tests a failsafe report may not be available + */ + junit testResults: '**/target/surefire-reports/TEST-*.xml', allowEmptyResults: true + junit testResults: '**/target/failsafe-reports/TEST-*.xml', allowEmptyResults: true + } + } + } + stage('Execute-Code-Coverage') { + // Ensure the code coverage is run only once per-commit + when { + allOf { + environment name: 'SERVER_VERSION', value: '4.0' + environment name: 'JABBA_VERSION', value: '1.8' + } + } + steps { + executeCodeCoverage() + } + } + } + } + post { + aborted { + notifySlack('aborted') + } + success { + notifySlack('completed') + } + unstable { + notifySlack('unstable') + } + failure { + notifySlack('FAILED') + } + } + } + } +} diff --git a/LICENSE b/LICENSE index d6456956733..a157e31d058 100644 --- a/LICENSE +++ b/LICENSE @@ -200,3 +200,24 @@ 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 Cassandra Java Driver bundles code and files from the following projects: + +JNR project +Copyright (C) 2008-2010 Wayne Meissner +This product includes software developed as part of the JNR project ( https://github.com/jnr/jnr-ffi )s. +see core/src/main/java/com/datastax/oss/driver/internal/core/os/CpuInfo.java + +Protocol Buffers +Copyright 2008 Google Inc. +This product includes software developed as part of the Protocol Buffers project ( https://developers.google.com/protocol-buffers/ ). +see core/src/main/java/com/datastax/oss/driver/internal/core/type/util/VIntCoding.java + +Guava +Copyright (C) 2007 The Guava Authors +This product includes software developed as part of the Guava project ( https://guava.dev ). +see core/src/main/java/com/datastax/oss/driver/internal/core/util/CountingIterator.java + +Copyright (C) 2018 Christian Stein +This product includes software developed by Christian Stein +see ci/install-jdk.sh diff --git a/LICENSE_binary b/LICENSE_binary new file mode 100644 index 00000000000..b59c6ec22bb --- /dev/null +++ b/LICENSE_binary @@ -0,0 +1,247 @@ + + 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 Cassandra Java Driver bundles code and files from the following projects: + +JNR project +Copyright (C) 2008-2010 Wayne Meissner +This product includes software developed as part of the JNR project ( https://github.com/jnr/jnr-ffi )s. +see core/src/main/java/com/datastax/oss/driver/internal/core/os/CpuInfo.java + +Protocol Buffers +Copyright 2008 Google Inc. +This product includes software developed as part of the Protocol Buffers project ( https://developers.google.com/protocol-buffers/ ). +see core/src/main/java/com/datastax/oss/driver/internal/core/type/util/VIntCoding.java + +Guava +Copyright (C) 2007 The Guava Authors +This product includes software developed as part of the Guava project ( https://guava.dev ). +see core/src/main/java/com/datastax/oss/driver/internal/core/util/CountingIterator.java + +Copyright (C) 2018 Christian Stein +This product includes software developed by Christian Stein +see ci/install-jdk.sh + +This product bundles Java Native Runtime - POSIX 3.1.15, +which is available under the Eclipse Public License version 2.0. +see licenses/jnr-posix.txt + +This product bundles jnr-x86asm 1.0.2, +which is available under the MIT License. +see licenses/jnr-x86asm.txt + +This product bundles ASM 9.2: a very small and fast Java bytecode manipulation framework, +which is available under the 3-Clause BSD License. +see licenses/asm.txt + +This product bundles HdrHistogram 2.1.12: A High Dynamic Range (HDR) Histogram, +which is available under the 2-Clause BSD License. +see licenses/HdrHistogram.txt + +This product bundles The Simple Logging Facade for Java (SLF4J) API 1.7.26, +which is available under the MIT License. +see licenses/slf4j-api.txt + +This product bundles Reactive Streams 1.0.3, +which is available under the MIT License. +see licenses/reactive-streams.txt diff --git a/NOTICE.txt b/NOTICE.txt new file mode 100644 index 00000000000..8e27ae3e52f --- /dev/null +++ b/NOTICE.txt @@ -0,0 +1,5 @@ +Apache Cassandra Java Driver +Copyright 2012- The Apache Software Foundation + +This product includes software developed at The Apache Software +Foundation (http://www.apache.org/). diff --git a/NOTICE_binary.txt b/NOTICE_binary.txt new file mode 100644 index 00000000000..f6f11c298f6 --- /dev/null +++ b/NOTICE_binary.txt @@ -0,0 +1,249 @@ +Apache Cassandra Java Driver +Copyright 2012- The Apache Software Foundation + +This product includes software developed at The Apache Software +Foundation (http://www.apache.org/). + +This compiled product also includes Apache-licensed dependencies +that contain the following NOTICE information: + +================================================================== +io.netty:netty-handler NOTICE.txt +================================================================== +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: + * https://www.slf4j.org/ + +This product contains a modified portion of 'Apache Harmony', an open source +Java SE, which can be obtained at: + + * NOTICE: + * license/NOTICE.harmony.txt + * LICENSE: + * license/LICENSE.harmony.txt (Apache License 2.0) + * HOMEPAGE: + * https://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/yawkat/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 optionally depends on 'zstd-jni', a zstd-jni Java compression +and decompression library, which can be obtained at: + + * LICENSE: + * license/LICENSE.zstd-jni.txt (Apache License 2.0) + * HOMEPAGE: + * https://github.com/luben/zstd-jni + +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: + * https://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 (Apache License 2.0) + * HOMEPAGE: + * https://github.com/jboss-remoting/jboss-marshalling + +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 Commons Logging', a logging +framework, which can be obtained at: + + * LICENSE: + * license/LICENSE.commons-logging.txt (Apache License 2.0) + * HOMEPAGE: + * https://commons.apache.org/logging/ + +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: + * https://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: + * https://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 version of 'HPACK', a Java implementation of +the HTTP/2 HPACK algorithm written by Cory Benfield. It can be obtained at: + + * LICENSE: + * license/LICENSE.hyper-hpack.txt (MIT License) + * HOMEPAGE: + * https://github.com/python-hyper/hpack/ + +This product contains a modified version of 'HPACK', a Java implementation of +the HTTP/2 HPACK algorithm written by Tatsuhiro Tsujikawa. It can be obtained at: + + * LICENSE: + * license/LICENSE.nghttp2-hpack.txt (MIT License) + * HOMEPAGE: + * https://github.com/nghttp2/nghttp2/ + +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 the Maven wrapper scripts from 'Maven Wrapper', that provides an easy way to ensure a user has everything necessary to run the Maven build. + + * LICENSE: + * license/LICENSE.mvn-wrapper.txt (Apache License 2.0) + * HOMEPAGE: + * https://github.com/takari/maven-wrapper + +This product contains the dnsinfo.h header file, that provides a way to retrieve the system DNS configuration on MacOS. +This private header is also used by Apple's open source + mDNSResponder (https://opensource.apple.com/tarballs/mDNSResponder/). + + * LICENSE: + * license/LICENSE.dnsinfo.txt (Apple Public Source License 2.0) + * HOMEPAGE: + * https://www.opensource.apple.com/source/configd/configd-453.19/dnsinfo/dnsinfo.h + +This product optionally depends on 'Brotli4j', Brotli compression and +decompression for Java., which can be obtained at: + + * LICENSE: + * license/LICENSE.brotli4j.txt (Apache License 2.0) + * HOMEPAGE: + * https://github.com/hyperxpro/Brotli4j diff --git a/README.md b/README.md index 9f7da99298c..bd09d98cf44 100644 --- a/README.md +++ b/README.md @@ -1,71 +1,88 @@ -# Datastax Java Driver for Apache Cassandra® +# Java Driver for Apache Cassandra® + +[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) +[![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.apache.cassandra/java-driver-core/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.apache.cassandra/java-driver-core) *If you're reading this on github.com, please note that this is the readme for the development version and that some features described here might not yet have been released. You can find the documentation for latest version through [DataStax Docs] or via the release tags, e.g. -[4.0.1](https://github.com/datastax/java-driver/tree/4.0.1).* +[4.17.0](https://github.com/datastax/java-driver/tree/4.17.0).* A modern, feature-rich and highly tunable Java client library for [Apache Cassandra®] \(2.1+) and -[DataStax Enterprise] \(4.7+), using exclusively Cassandra's binary protocol and Cassandra Query -Language v3. +[DataStax Enterprise] \(4.7+), and [DataStax Astra], using exclusively Cassandra's binary protocol +and Cassandra Query Language (CQL) v3. [DataStax Docs]: http://docs.datastax.com/en/developer/java-driver/ [Apache Cassandra®]: http://cassandra.apache.org/ -[DataStax Enterprise]: http://www.datastax.com/products/datastax-enterprise ## Getting the driver -The driver artifacts are published in Maven central, under the group id [com.datastax.oss]; there +The driver artifacts are published in Maven central, under the group id [org.apache.cassandra]; there are multiple modules, all prefixed with `java-driver-`. ```xml - com.datastax.oss + org.apache.cassandra java-driver-core - 4.0.1 + ${driver.version} - com.datastax.oss + org.apache.cassandra java-driver-query-builder - 4.0.1 + ${driver.version} + + + + org.apache.cassandra + java-driver-mapper-runtime + ${driver.version} ``` -Refer to the [manual] for more details. +Note that the query builder is now published as a separate artifact, you'll need to add the +dependency if you plan to use it. + +Refer to each module's manual for more details ([core](manual/core/README.md), [query +builder](manual/query_builder/README.md), [mapper](manual/mapper/README.md)). + +[org.apache.cassandra]: http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.apache.cassandra%22 -[com.datastax.oss]: http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.datastax.oss%22 -[manual]: manual/ +## Compatibility + +The driver is compatible with Apache Cassandra® 2.1 and higher, DataStax Enterprise 4.7 and +higher, and DataStax Astra. + +It requires Java 8 or higher. + +Disclaimer: Some DataStax/DataStax Enterprise products might partially work on big-endian systems, +but DataStax does not officially support these systems. ## Migrating from previous versions -Java driver 4 is **not binary compatible** with previous versions. However, most of the concepts +Java Driver 4 is **not binary compatible** with previous versions. However, most of the concepts remain unchanged, and the new API will look very familiar to 2.x and 3.x users. -See the [upgrade guide](upgrade_guide/) for details. +See the [upgrade guide](upgrade_guide/README.md) for details. ## Useful links -* [Manual][manual] +* [Manual](manual/README.md) * [API docs] * Bug tracking: [JIRA] * [Mailing list] -* Twitter: [@dsJavaDriver] tweets Java driver releases and important announcements (low frequency). - [@DataStaxEng] has more news, including other drivers, Cassandra, and DSE. * [Changelog] * [FAQ] -[API docs]: http://www.datastax.com/drivers/java/4.0 -[JIRA]: https://datastax-oss.atlassian.net/browse/JAVA -[Mailing list]: https://groups.google.com/a/lists.datastax.com/forum/#!forum/java-driver-user -[@dsJavaDriver]: https://twitter.com/dsJavaDriver -[@DataStaxEng]: https://twitter.com/datastaxeng -[Changelog]: changelog/ -[FAQ]: faq/ +[API docs]: https://docs.datastax.com/en/drivers/java/4.17 +[JIRA]: https://issues.apache.org/jira/issues/?jql=project%20%3D%20CASSJAVA%20ORDER%20BY%20key%20DESC +[Mailing list]: https://lists.apache.org/list.html?user@cassandra.apache.org +[Changelog]: changelog/README.md +[FAQ]: faq/README.md ## License -Copyright 2017, DataStax +© The Apache Software Foundation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -81,9 +98,8 @@ limitations under the License. ---- -DataStax is a registered trademark of DataStax, Inc. and its subsidiaries in the United States -and/or other countries. - Apache Cassandra, Apache, Tomcat, Lucene, Solr, Hadoop, Spark, TinkerPop, and Cassandra are trademarks of the [Apache Software Foundation](http://www.apache.org/) or its subsidiaries in Canada, the United States and/or other countries. + +Binary artifacts of this product bundle Java Native Runtime libraries, which is available under the Eclipse Public License version 2.0. diff --git a/bom/pom.xml b/bom/pom.xml new file mode 100644 index 00000000000..67e26c95bde --- /dev/null +++ b/bom/pom.xml @@ -0,0 +1,121 @@ + + + + 4.0.0 + + org.apache.cassandra + java-driver-parent + 4.19.4-SNAPSHOT + + java-driver-bom + pom + Apache Cassandra Java Driver - Bill Of Materials + + + + org.apache.cassandra + java-driver-core + 4.19.4-SNAPSHOT + + + org.apache.cassandra + java-driver-core-shaded + 4.19.4-SNAPSHOT + + + org.apache.cassandra + java-driver-mapper-processor + 4.19.4-SNAPSHOT + + + org.apache.cassandra + java-driver-mapper-runtime + 4.19.4-SNAPSHOT + + + org.apache.cassandra + java-driver-query-builder + 4.19.4-SNAPSHOT + + + org.apache.cassandra + java-driver-guava-shaded + 4.19.4-SNAPSHOT + + + org.apache.cassandra + java-driver-test-infra + 4.19.4-SNAPSHOT + + + org.apache.cassandra + java-driver-metrics-micrometer + 4.19.4-SNAPSHOT + + + org.apache.cassandra + java-driver-metrics-microprofile + 4.19.4-SNAPSHOT + + + com.datastax.oss + native-protocol + 1.5.2 + + + + + + + org.codehaus.mojo + flatten-maven-plugin + + + flatten + process-resources + + flatten + + + + keep + expand + expand + expand + expand + expand + expand + expand + expand + expand + expand + expand + expand + remove + + true + + + + + + + diff --git a/build-doc.sh b/build-doc.sh new file mode 100755 index 00000000000..3be00f379b9 --- /dev/null +++ b/build-doc.sh @@ -0,0 +1,16 @@ +set -e +# Set up python environment +#pyenv local 3.10.1 +#pip install mkdocs mkdocs-material mkdocs-awesome-pages-plugin mkdocs-macros-plugin + +# In some bash/zsh environments, the locale is not set correctly, which causes mkdocs to fail. +export LC_ALL=en_US.UTF-8 +export LANG=en_US.UTF-8 + +# Build Javadoc +mvn clean install -DskipTests # or guava-shaded can not be found +# mvn javadoc:javadoc -pl core,query-builder,mapper-runtime +mvn javadoc:aggregate + +# Build manual with API references +mkdocs build -s # strict, so it fails with warning. You can also use `mkdocs serve` to preview diff --git a/build.yaml b/build.yaml deleted file mode 100644 index 79dd3b2c84e..00000000000 --- a/build.yaml +++ /dev/null @@ -1,22 +0,0 @@ -java: - - openjdk8 -os: - - ubuntu/bionic64/java-driver -cassandra: - - '2.1' - - '2.2' - - '3.0' - - '3.11' -build: - - type: maven - version: 3.2.5 - goals: verify --batch-mode - properties: | - ccm.version=$CCM_CASSANDRA_VERSION - - xunit: - - "**/target/surefire-reports/TEST-*.xml" - - "**/target/failsafe-reports/TEST-*.xml" - - jacoco: true -disable_commit_status: true -notify: - slack: java-driver-dev-bots diff --git a/changelog/README.md b/changelog/README.md index 9afeb2e9d64..7ca167d00cc 100644 --- a/changelog/README.md +++ b/changelog/README.md @@ -1,9 +1,503 @@ + + ## Changelog -### 4.0.2 (in progress) +### 4.19.3 + +- [bug] CASSJAVA-3: Fix ordering of LIMIT and PER PARTITION LIMIT clauses +- [bug] PR 2050: Fix race condition in ClockSeqAndNodeContainer +- [improvement] CASSJAVA-108: Update ESRI (and remove org.json) dependencies +- [improvement] PR 2076: Bump Jackson to 2.20.1 +- [improvement] PR 2047: Re-work ordering clause support in query builder +- [improvement] PR 2051: Bump logback to 1.3.15, slf4j to 2.0.16 +- [improvement] CASSJAVA-113: Bump LZ4 to 1.10.1, Netty to 4.1.130.Final + +### 4.19.2 + +- [bug] CASSJAVA-116: Retry or Speculative Execution with RequestIdGenerator throws "Duplicate Key" + +### 4.19.1 + +- [improvement] CASSJAVA-97: Let users inject an ID for each request and write to the custom payload +- [improvement] CASSJAVA-92: Add Local DC to driver connection info and provide visibility with nodetool clientstats +- [bug] PR 2025: Eliminate lock in ConcurrencyLimitingRequestThrottler +- [improvement] CASSJAVA-89: Fix deprecated table configs in Cassandra 5 +- [improvement] PR 2028: Remove unnecessary locking in DefaultNettyOptions +- [improvement] CASSJAVA-102: Fix revapi spurious complaints about optional dependencies +- [improvement] PR 2013: Add SubnetAddressTranslator +- [improvement] CASSJAVA-68: Improve DefaultCodecRegistry.CacheKey#hashCode() to eliminate Object[] allocation +- [improvement] PR 1989: Bump Jackson version to la(te)st 2.13.x, 2.13.5 +- [improvement] CASSJAVA-76: Make guava an optional dependency of java-driver-guava-shaded +- [bug] PR 2035: Prevent long overflow in SNI address resolution +- [improvement] CASSJAVA-77: 4.x: Upgrade Netty to 4.1.119 +- [improvement] CASSJAVA-40: Driver testing against Java 21 +- [improvement] CASSJAVA-90: Update native-protocol +- [improvement] CASSJAVA-80: Support configuration to disable DNS reverse-lookups for SAN validation + +### 4.19.0 + +- [bug] JAVA-3055: Prevent PreparedStatement cache to be polluted if a request is cancelled. +- [bug] JAVA-3168: Copy node info for contact points on initial node refresh only from first match by endpoint +- [improvement] JAVA-3143: Extend driver vector support to arbitrary subtypes and fix handling of variable length types (OSS C* 5.0) +- [improvement] CASSJAVA-53: Update Guava version used in cassandra-java-driver +- [improvement] JAVA-3118: Add support for vector data type in Schema Builder, QueryBuilder +- [bug] CASSJAVA-55: Remove setting "Host" header for metadata requests +- [bug] JAVA-3057: Allow decoding a UDT that has more fields than expected +- [improvement] CASSJAVA-52: Bring java-driver-shaded-guava into the repo as a submodule +- [bug] CASSJAVA-2: TableMetadata#describe produces invalid CQL when a type of a column is a vector +- [bug] JAVA-3051: Memory leak in DefaultLoadBalancingPolicy measurement of response times +- [improvement] CASSJAVA-14: Query builder support for NOT CQL syntax +- [bug] CASSJAVA-12: DefaultSslEngineFactory missing null check on close +- [improvement] CASSJAVA-46: Expose table extensions via schema builders +- [bug] PR 1938: Fix uncaught exception during graceful channel shutdown after exceeding max orphan ids +- [improvement] PR 1607: Annotate BatchStatement, Statement, SimpleStatement methods with CheckReturnValue +- [improvement] CASSJAVA-41: Reduce lock held duration in ConcurrencyLimitingRequestThrottler +- [bug] JAVA-3149: Async Query Cancellation Not Propagated To RequestThrottler +- [bug] JAVA-3167: CompletableFutures.allSuccessful() may return never completed future +- [bug] PR 1620: Don't return empty routing key when partition key is unbound +- [improvement] PR 1623: Limit calls to Conversions.resolveExecutionProfile +- [improvement] CASSJAVA-29: Update target Cassandra versions for integration tests, support new 5.0.x + +### 4.18.1 + +- [improvement] JAVA-3142: Ability to specify ordering of remote local dc's via new configuration for graceful automatic failovers +- [bug] CASSANDRA-19457: Object reference in Micrometer metrics prevent GC from reclaiming Session instances +- [improvement] CASSANDRA-19468: Don't swallow exception during metadata refresh +- [bug] CASSANDRA-19333: Fix data corruption in VectorCodec when using heap buffers +- [improvement] CASSANDRA-19290: Replace uses of AttributeKey.newInstance +- [improvement] CASSANDRA-19352: Support native_transport_(address|port) + native_transport_port_ssl for DSE 6.8 (4.x edition) +- [improvement] CASSANDRA-19180: Support reloading keystore in cassandra-java-driver + +### 4.18.0 + +- [improvement] PR 1689: Add support for publishing percentile time series for the histogram metrics (nparaddi-walmart) +- [improvement] JAVA-3104: Do not eagerly pre-allocate array when deserializing CqlVector +- [improvement] JAVA-3111: upgrade jackson-databind to 2.13.4.2 to address gradle dependency issue +- [improvement] PR 1617: Improve ByteBufPrimitiveCodec readBytes (chibenwa) +- [improvement] JAVA-3095: Fix CREATE keyword in vector search example in upgrade guide +- [improvement] JAVA-3100: Update jackson-databind to 2.13.4.1 and jackson-jaxrs-json-provider to 2.13.4 to address recent CVEs +- [improvement] JAVA-3089: Forbid wildcard imports + +### 4.17.0 + +- [improvement] JAVA-3070: Make CqlVector and CqlDuration serializable +- [improvement] JAVA-3085: Initialize c.d.o.d.i.core.util.Dependency at Graal native image build-time +- [improvement] JAVA-3061: CqlVector API improvements, add support for accessing vectors directly as float arrays +- [improvement] JAVA-3042: Enable automated testing for Java17 +- [improvement] JAVA-3050: Upgrade Netty to 4.1.94 + +### 4.16.0 + +- [improvement] JAVA-3058: Clear prepared statement cache on UDT type change event +- [improvement] JAVA-3060: Add vector type, codec + support for parsing CQL type +- [improvement] DOC-2813: Add error handling guidance linking to a helpful blog post +- [improvement] JAVA-3045: Fix GraalVM native image support for GraalVM 22.2 + +### 4.15.0 + +- [improvement] JAVA-3041: Update Guava session sample code to use ProgrammaticArguments +- [improvement] JAVA-3022: Implement AddressTranslator for AWS PrivateLink +- [bug] JAVA-3021: Update table SchemaBuilder page to replace withPrimaryKey with withPartitionKey +- [bug] JAVA-3005: Node list refresh behavior in 4.x is different from 3.x +- [bug] JAVA-3002: spring-boot app keeps connecting to IP of replaced node +- [improvement] JAVA-3023 Upgrade Netty to 4.1.77 +- [improvement] JAVA-2995: CodecNotFoundException doesn't extend DriverException + +### 4.14.1 + +- [improvement] JAVA-3013: Upgrade dependencies to address CVEs and other security issues, 4.14.1 edition +- [improvement] JAVA-2977: Update Netty to resolve higher-priority CVEs +- [improvement] JAVA-3003: Update jnr-posix to address CVE-2014-4043 + +### 4.14.0 + +- [bug] JAVA-2976: Support missing protocol v5 error codes CAS_WRITE_UNKNOWN, CDC_WRITE_FAILURE +- [bug] JAVA-2987: BasicLoadBalancingPolicy remote computation assumes local DC is up and live +- [bug] JAVA-2992: Include options into DefaultTableMetadata equals and hash methods +- [improvement] JAVA-2982: Switch Esri geometry lib to an optional dependency +- [improvement] JAVA-2959: Don't throw NoNodeAvailableException when all connections busy + +### 4.13.0 + +- [improvement] JAVA-2940: Add GraalVM native image build configurations +- [improvement] JAVA-2953: Promote ProgrammaticPlainTextAuthProvider to the public API and add + credentials hot-reload +- [improvement] JAVA-2951: Accept multiple node state listeners, schema change listeners and request + trackers + +Merged from 4.12.x: + +- [bug] JAVA-2949: Provide mapper support for CompletionStage> +- [bug] JAVA-2950: Remove reference to Reflection class from DependencyCheck + +### 4.12.1 + +Merged from 4.11.x: + +- [bug] JAVA-2949: Provide mapper support for CompletionStage> +- [bug] JAVA-2950: Remove reference to Reflection class from DependencyCheck + +### 4.12.0 + +- [improvement] JAVA-2935: Make GetEntity and SetEntity methods resilient to incomplete data +- [improvement] JAVA-2944: Upgrade MicroProfile Metrics to 3.0 + +Merged from 4.11.x: + +- [bug] JAVA-2932: Make DefaultDriverConfigLoader.close() resilient to terminated executors +- [bug] JAVA-2945: Reinstate InternalDriverContext.getNodeFilter method +- [bug] JAVA-2947: Release buffer after decoding multi-slice frame +- [bug] JAVA-2946: Make MapperResultProducerService instances be located with user-provided class loader +- [bug] JAVA-2942: GraphStatement.setConsistencyLevel() is not effective +- [bug] JAVA-2941: Cannot add a single static column with the alter table API +- [bug] JAVA-2943: Prevent session leak with wrong keyspace name +- [bug] JAVA-2938: OverloadedException message is misleading + +### 4.11.3 + +- [bug] JAVA-2949: Provide mapper support for CompletionStage> +- [bug] JAVA-2950: Remove reference to Reflection class from DependencyCheck + +### 4.11.2 + +- [bug] JAVA-2932: Make DefaultDriverConfigLoader.close() resilient to terminated executors +- [bug] JAVA-2945: Reinstate InternalDriverContext.getNodeFilter method +- [bug] JAVA-2947: Release buffer after decoding multi-slice frame +- [bug] JAVA-2946: Make MapperResultProducerService instances be located with user-provided class loader +- [bug] JAVA-2942: GraphStatement.setConsistencyLevel() is not effective +- [bug] JAVA-2941: Cannot add a single static column with the alter table API +- [bug] JAVA-2943: Prevent session leak with wrong keyspace name +- [bug] JAVA-2938: OverloadedException message is misleading + +### 4.11.1 + +- [bug] JAVA-2910: Add a configuration option to support strong values for prepared statements cache +- [bug] JAVA-2936: Support Protocol V6 +- [bug] JAVA-2934: Handle empty non-final pages in ReactiveResultSetSubscription + +### 4.11.0 + +- [improvement] JAVA-2930: Allow Micrometer to record histograms for timers +- [improvement] JAVA-2914: Transform node filter into a more flexible node distance evaluator +- [improvement] JAVA-2929: Revisit node-level metric eviction +- [new feature] JAVA-2830: Add mapper support for Java streams +- [bug] JAVA-2928: Generate counter increment/decrement constructs compatible with legacy C* + versions +- [new feature] JAVA-2872: Ability to customize metric names and tags +- [bug] JAVA-2925: Consider protocol version unsupported when server requires USE_BETA flag for it +- [improvement] JAVA-2704: Remove protocol v5 beta status, add v6-beta +- [improvement] JAVA-2916: Annotate generated classes with `@SuppressWarnings` +- [bug] JAVA-2927: Make Dropwizard truly optional +- [improvement] JAVA-2917: Include GraalVM substitutions for request processors and geo codecs +- [bug] JAVA-2918: Exclude invalid peers from schema agreement checks + +### 4.10.0 + +- [improvement] JAVA-2907: Switch Tinkerpop to an optional dependency +- [improvement] JAVA-2904: Upgrade Jackson to 2.12.0 and Tinkerpop to 3.4.9 +- [bug] JAVA-2911: Prevent control connection from scheduling too many reconnections +- [bug] JAVA-2902: Consider computed values when validating constructors for immutable entities +- [new feature] JAVA-2899: Re-introduce cross-DC failover in driver 4 +- [new feature] JAVA-2900: Re-introduce consistency downgrading retries +- [new feature] JAVA-2903: BlockHound integration +- [improvement] JAVA-2877: Allow skipping validation for individual mapped entities +- [improvement] JAVA-2871: Allow keyspace exclusions in the metadata, and exclude system keyspaces + by default +- [improvement] JAVA-2449: Use non-cryptographic random number generation in Uuids.random() +- [improvement] JAVA-2893: Allow duplicate keys in DefaultProgrammaticDriverConfigLoaderBuilder +- [documentation] JAVA-2894: Clarify usage of Statement.setQueryTimestamp +- [bug] JAVA-2889: Remove TypeSafe imports from DriverConfigLoader +- [bug] JAVA-2883: Use root locale explicitly when changing string case +- [bug] JAVA-2890: Fix off-by-one error in UdtCodec +- [improvement] JAVA-2905: Prevent new connections from using a protocol version higher than the negotiated one +- [bug] JAVA-2647: Handle token types in QueryBuilder.literal() +- [bug] JAVA-2887: Handle composite profiles with more than one key and/or backed by only one profile + +### 4.9.0 + +- [documentation] JAVA-2823: Make Astra more visible in the docs +- [documentation] JAVA-2869: Advise against using 4.5.x-4.6.0 in the upgrade guide +- [documentation] JAVA-2868: Cover reconnect-on-init in the manual +- [improvement] JAVA-2827: Exclude unused Tinkerpop transitive dependencies +- [improvement] JAVA-2827: Remove dependency to Tinkerpop gremlin-driver +- [task] JAVA-2859: Upgrade Tinkerpop to 3.4.8 +- [bug] JAVA-2726: Fix Tinkerpop incompatibility with JPMS +- [bug] JAVA-2842: Remove security vulnerabilities introduced by Tinkerpop +- [bug] JAVA-2867: Revisit compressor substitutions +- [improvement] JAVA-2870: Optimize memory usage of token map +- [improvement] JAVA-2855: Allow selection of the metrics framework via the config +- [improvement] JAVA-2864: Revisit mapper processor's messaging +- [new feature] JAVA-2816: Support immutability and fluent accessors in the mapper +- [new feature] JAVA-2721: Add counter support in the mapper +- [bug] JAVA-2863: Reintroduce mapper processor dependency to SLF4J + +### 4.8.0 + +- [improvement] JAVA-2811: Add aliases for driver 3 method names +- [new feature] JAVA-2808: Provide metrics bindings for Micrometer and MicroProfile +- [new feature] JAVA-2773: Support new protocol v5 message format +- [improvement] JAVA-2841: Raise timeouts during connection initialization +- [bug] JAVA-2331: Unregister old metrics when a node gets removed or changes RPC address +- [improvement] JAVA-2850: Ignore credentials in secure connect bundle [DataStax Astra] +- [improvement] JAVA-2813: Don't fail when secure bundle is specified together with other options +- [bug] JAVA-2800: Exclude SLF4J from mapper-processor dependencies +- [new feature] JAVA-2819: Add DriverConfigLoader.fromString +- [improvement] JAVA-2431: Set all occurrences when bound variables are used multiple times +- [improvement] JAVA-2829: Log protocol negotiation messages at DEBUG level +- [bug] JAVA-2846: Give system properties the highest precedence in DefaultDriverConfigLoader +- [new feature] JAVA-2691: Provide driver 4 support for extra codecs +- [improvement] Allow injection of CodecRegistry on session builder +- [improvement] JAVA-2828: Add safe paging state wrapper +- [bug] JAVA-2835: Correctly handle unresolved addresses in DefaultEndPoint.equals +- [bug] JAVA-2838: Avoid ConcurrentModificationException when closing connection +- [bug] JAVA-2837: make StringCodec strict about unicode in ascii + +### 4.7.2 + +- [bug] JAVA-2821: Can't connect to DataStax Astra using driver 4.7.x + +### 4.7.1 + +- [bug] JAVA-2818: Remove root path only after merging non-programmatic configs +### 4.7.0 + +- [improvement] JAVA-2301: Introduce OSGi tests for the mapper +- [improvement] JAVA-2658: Refactor OSGi tests +- [bug] JAVA-2657: Add ability to specify the class loader to use for application-specific classpath resources +- [improvement] JAVA-2803: Add Graal substitutions for protocol compression +- [documentation] JAVA-2666: Document BOM and driver modules +- [documentation] JAVA-2613: Improve connection pooling documentation +- [new feature] JAVA-2793: Add composite config loader +- [new feature] JAVA-2792: Allow custom results in the mapper +- [improvement] JAVA-2663: Add Graal substitutions for native functions +- [improvement] JAVA-2747: Revisit semantics of Statement.setExecutionProfile/Name + +### 4.6.1 + +- [bug] JAVA-2676: Don't reschedule write coalescer after empty runs + +### 4.6.0 + +- [improvement] JAVA-2741: Make keyspace/table metadata impls serializable +- [bug] JAVA-2740: Extend peer validity check to include datacenter, rack and tokens +- [bug] JAVA-2744: Recompute token map when node is added +- [new feature] JAVA-2614: Provide a utility to emulate offset paging on the client side +- [new feature] JAVA-2718: Warn when the number of sessions exceeds a configurable threshold +- [improvement] JAVA-2664: Add a callback to inject the session in listeners +- [bug] JAVA-2698: TupleCodec and UdtCodec give wrong error message when parsing fails +- [improvement] JAVA-2435: Add automatic-module-names to the manifests +- [new feature] JAVA-2054: Add now_in_seconds to protocol v5 query messages +- [bug] JAVA-2711: Fix handling of UDT keys in the mapper +- [improvement] JAVA-2631: Add getIndex() shortcuts to TableMetadata +- [improvement] JAVA-2679: Add port information to QueryTrace and TraceEvent +- [improvement] JAVA-2184: Refactor DescribeIT to improve maintainability +- [new feature] JAVA-2600: Add map-backed config loader +- [new feature] JAVA-2105: Add support for transient replication +- [new feature] JAVA-2670: Provide base class for mapped custom codecs +- [new feature] JAVA-2633: Add execution profile argument to DAO mapper factory methods +- [improvement] JAVA-2667: Add ability to fail the build when integration tests fail +- [bug] JAVA-1861: Add Metadata.getClusterName() + +### 4.5.1 + +- [bug] JAVA-2673: Fix mapper generated code for UPDATE with TTL and IF condition + +### 4.5.0 + +- [bug] JAVA-2654: Make AdminRequestHandler handle integer serialization +- [improvement] JAVA-2618: Improve error handling in request handlers +- [new feature] JAVA-2064: Add support for DSE 6.8 graph options in schema builder +- [documentation] JAVA-2559: Fix GraphNode javadocs +- [improvement] JAVA-2281: Extend GraphBinaryDataTypesTest to other graph protocols +- [new feature] JAVA-2498: Add support for reactive graph queries +- [bug] JAVA-2572: Prevent race conditions when cancelling a continuous paging query +- [improvement] JAVA-2566: Introduce specific metrics for Graph queries +- [improvement] JAVA-2556: Make ExecutionInfo compatible with any Request type +- [improvement] JAVA-2571: Revisit usages of DseGraph.g +- [improvement] JAVA-2558: Revisit GraphRequestHandler +- [bug] JAVA-2508: Preserve backward compatibility in schema metadata types +- [bug] JAVA-2465: Avoid requesting 0 page when executing continuous paging queries +- [improvement] JAVA-2472: Enable speculative executions for paged graph queries +- [improvement] JAVA-1579: Change default result format to latest GraphSON format +- [improvement] JAVA-2496: Revisit timeouts for paged graph queries +- [bug] JAVA-2510: Fix GraphBinaryDataTypesTest Codec registry initialization +- [bug] JAVA-2492: Parse edge metadata using internal identifiers +- [improvement] JAVA-2282: Remove GraphSON3 support +- [new feature] JAVA-2098: Add filter predicates for collections +- [improvement] JAVA-2245: Rename graph engine Legacy to Classic and Modern to Core +- [new feature] JAVA-2099: Enable Paging Through DSE Driver for Gremlin Traversals (2.x) +- [new feature] JAVA-1898: Expose new table-level graph metadata +- [bug] JAVA-2642: Fix default value of max-orphan-requests +- [bug] JAVA-2644: Revisit channel selection when pool size > 1 +- [bug] JAVA-2630: Correctly handle custom classes in IndexMetadata.describe +- [improvement] JAVA-1556: Publish Maven Bill Of Materials POM +- [improvement] JAVA-2637: Bump Netty to 4.1.45 +- [bug] JAVA-2617: Reinstate generation of deps.txt for Insights +- [new feature] JAVA-2625: Provide user-friendly programmatic configuration for kerberos +- [improvement] JAVA-2624: Expose a config option for the connect timeout +- [improvement] JAVA-2592: Make reload support parameterizable for DefaultDriverConfigLoader +- [new feature] JAVA-2263: Add optional schema validation to the mapper + +### 4.4.0 + +This version brings in all functionality that was formerly only in the DataStax Enterprise driver, +such as the built-in support for reactive programming. Going forward, all new features will be +implemented in this single driver (for past DataStax Enterprise driver versions before the merge, +refer to the [DSE driver +changelog](https://docs.datastax.com/en/developer/java-driver-dse/latest/changelog/)). + +- [documentation] JAVA-2607: Improve visibility of driver dependencies section +- [documentation] JAVA-1975: Document the importance of using specific TinkerPop version +- [improvement] JAVA-2529: Standardize optional/excludable dependency checks +- [bug] JAVA-2598: Do not use context class loader when attempting to load classes +- [improvement] JAVA-2582: Don't propagate a future into SchemaQueriesFactory +- [documentation] JAVA-2542: Improve the javadocs of methods in CqlSession +- [documentation] JAVA-2609: Add docs for proxy authentication to unified driver +- [improvement] JAVA-2554: Improve efficiency of InsightsClient by improving supportsInsights check +- [improvement] JAVA-2601: Inject Google Tag Manager scripts in generated API documentation +- [improvement] JAVA-2551: Improve support for DETERMINISTIC and MONOTONIC functions +- [documentation] JAVA-2446: Revisit continuous paging javadocs +- [improvement] JAVA-2550: Remove warnings in ContinuousCqlRequestHandler when coordinator is not replica +- [improvement] JAVA-2569: Make driver compatible with Netty < 4.1.34 again +- [improvement] JAVA-2541: Improve error messages during connection initialization +- [improvement] JAVA-2530: Expose shortcuts for name-based UUIDs +- [improvement] JAVA-2547: Add method DriverConfigLoader.fromPath +- [improvement] JAVA-2528: Store suppressed exceptions in AllNodesFailedException +- [new feature] JAVA-2581: Add query builder support for indexed list assignments +- [improvement] JAVA-2596: Consider collection removals as idempotent in query builder +- [bug] JAVA-2555: Generate append/prepend constructs compatible with legacy C* versions +- [bug] JAVA-2584: Ensure codec registry is able to create codecs for collections of UDTs and tuples +- [bug] JAVA-2583: IS NOT NULL clause should be idempotent +- [improvement] JAVA-2442: Don't check for schema agreement twice when completing a DDL query +- [improvement] JAVA-2473: Don't reconnect control connection if protocol is downgraded +- [bug] JAVA-2556: Make ExecutionInfo compatible with any Request type +- [new feature] JAVA-2532: Add BoundStatement ReturnType for insert, update, and delete DAO methods +- [improvement] JAVA-2107: Add XML formatting plugin +- [bug] JAVA-2527: Allow AllNodesFailedException to accept more than one error per node +- [improvement] JAVA-2546: Abort schema refresh if a query fails + +### 4.3.1 + +- [bug] JAVA-2557: Accept any negative length when decoding elements of tuples and UDTs + +### 4.3.0 + +- [improvement] JAVA-2497: Ensure nodes and exceptions are serializable +- [bug] JAVA-2464: Fix initial schema refresh when reconnect-on-init is enabled +- [improvement] JAVA-2516: Enable hostname validation with Cloud +- [documentation]: JAVA-2460: Document how to determine the local DC +- [improvement] JAVA-2476: Improve error message when codec registry inspects a collection with a + null element +- [documentation] JAVA-2509: Mention file-based approach for Cloud configuration in the manual +- [improvement] JAVA-2447: Mention programmatic local DC method in Default LBP error message +- [improvement] JAVA-2459: Improve extensibility of existing load balancing policies +- [documentation] JAVA-2428: Add developer docs +- [documentation] JAVA-2503: Migrate Cloud "getting started" page to driver manual +- [improvement] JAVA-2484: Add errors for cloud misconfiguration +- [improvement] JAVA-2490: Allow to read the secure bundle from an InputStream +- [new feature] JAVA-2478: Allow to provide the secure bundle via URL +- [new feature] JAVA-2356: Support for DataStax Cloud API +- [improvement] JAVA-2407: Improve handling of logback configuration files in IDEs +- [improvement] JAVA-2434: Add support for custom cipher suites and host name validation to ProgrammaticSslEngineFactory +- [improvement] JAVA-2480: Upgrade Jackson to 2.10.0 +- [documentation] JAVA-2505: Annotate Node.getHostId() as nullable +- [improvement] JAVA-1708: Support DSE "everywhere" replication strategy +- [improvement] JAVA-2471: Consider DSE version when parsing the schema +- [improvement] JAVA-2444: Add method setRoutingKey(ByteBuffer...) to StatementBuilder +- [improvement] JAVA-2398: Improve support for optional dependencies in OSGi +- [improvement] JAVA-2452: Allow "none" as a compression option +- [improvement] JAVA-2419: Allow registration of user codecs at runtime +- [documentation] JAVA-2384: Add quick overview section to each manual page +- [documentation] JAVA-2412: Cover DDL query debouncing in FAQ and upgrade guide +- [documentation] JAVA-2416: Update paging section in the manual +- [improvement] JAVA-2402: Add setTracing(boolean) to StatementBuilder +- [bug] JAVA-2466: Set idempotence to null in BatchStatement.newInstance + +### 4.2.2 + +- [bug] JAVA-2475: Fix message size when query string contains Unicode surrogates +- [bug] JAVA-2470: Fix Session.OSS_DRIVER_COORDINATES for shaded JAR + +### 4.2.1 + +- [bug] JAVA-2454: Handle "empty" CQL type while parsing schema +- [improvement] JAVA-2455: Improve logging of schema refresh errors +- [documentation] JAVA-2429: Document expected types on DefaultDriverOption +- [documentation] JAVA-2426: Fix month pattern in CqlDuration documentation +- [bug] JAVA-2451: Make zero a valid estimated size for PagingIterableSpliterator +- [bug] JAVA-2443: Compute prepared statement PK indices for protocol v3 +- [bug] JAVA-2430: Use variable metadata to infer the routing keyspace on bound statements + +### 4.2.0 + +- [improvement] JAVA-2390: Add methods to set the SSL engine factory programmatically +- [improvement] JAVA-2379: Fail fast if prepared id doesn't match when repreparing on the fly +- [bug] JAVA-2375: Use per-request keyspace when repreparing on the fly +- [improvement] JAVA-2370: Remove auto-service plugin from mapper processor +- [improvement] JAVA-2377: Add a config option to make driver threads daemon +- [improvement] JAVA-2371: Handle null elements in collections on the decode path +- [improvement] JAVA-2351: Add a driver example for the object mapper +- [bug] JAVA-2323: Handle restart of a node with same host_id but a different address +- [improvement] JAVA-2303: Ignore peer rows matching the control host's RPC address +- [improvement] JAVA-2236: Add methods to set the auth provider programmatically +- [improvement] JAVA-2369: Change mapper annotations retention to runtime +- [improvement] JAVA-2365: Redeclare default constants when an enum is abstracted behind an + interface +- [improvement] JAVA-2302: Better target mapper errors and warnings for inherited methods +- [improvement] JAVA-2336: Expose byte utility methods in the public API +- [improvement] JAVA-2338: Revisit toString() for data container types +- [bug] JAVA-2367: Fix column names in EntityHelper.updateByPrimaryKey +- [bug] JAVA-2358: Fix list of reserved CQL keywords +- [improvement] JAVA-2359: Allow default keyspace at the mapper level +- [improvement] JAVA-2306: Clear security tokens from memory immediately after use +- [improvement] JAVA-2320: Expose more attributes on mapper Select for individual query clauses +- [bug] JAVA-2332: Destroy connection pool when a node gets removed +- [bug] JAVA-2324: Add support for primitive shorts in mapper +- [bug] JAVA-2325: Allow "is" prefix for boolean getters in mapped entities +- [improvement] JAVA-2308: Add customWhereClause to `@Delete` +- [improvement] JAVA-2247: PagingIterable implementations should implement spliterator() +- [bug] JAVA-2312: Handle UDTs with names that clash with collection types +- [improvement] JAVA-2307: Improve `@Select` and `@Delete` by not requiring full primary key +- [improvement] JAVA-2315: Improve extensibility of session builder +- [bug] JAVA-2394: BaseCcmRule DseRequirement max should use DseVersion, not Cassandra version + +### 4.1.0 + +- [documentation] JAVA-2294: Fix wrong examples in manual page on batch statements +- [bug] JAVA-2304: Avoid direct calls to ByteBuffer.array() +- [new feature] JAVA-2078: Add object mapper +- [improvement] JAVA-2297: Add a NettyOptions method to set socket options +- [bug] JAVA-2280: Ignore peer rows with missing host id or RPC address +- [bug] JAVA-2264: Adjust HashedWheelTimer tick duration from 1 to 100 ms +- [bug] JAVA-2260: Handle empty collections in PreparedStatement.bind(...) +- [improvement] JAVA-2278: Pass the request's log prefix to RequestTracker +- [bug] JAVA-2253: Don't strip trailing zeros in ByteOrderedToken +- [improvement] JAVA-2207: Add bulk value assignment to QueryBuilder Insert +- [bug] JAVA-2234: Handle terminated executor when the session is closed twice +- [documentation] JAVA-2220: Emphasize that query builder is now a separate artifact in root README +- [documentation] JAVA-2217: Cover contact points and local datacenter earlier in the manual +- [improvement] JAVA-2242: Allow skipping all integration tests with -DskipITs +- [improvement] JAVA-2241: Make DefaultDriverContext.cycleDetector protected - [bug] JAVA-2226: Support IPv6 contact points in the configuration ### 4.0.1 @@ -232,3 +726,1698 @@ - [new feature] JAVA-1501: Reprepare on the fly when we get an UNPREPARED response - [bug] JAVA-1499: Wait for load balancing policy at cluster initialization - [new feature] JAVA-1495: Add prepared statements + +## 3.11.5 +- [improvement] JAVA-3114: Shade io.dropwizard.metrics:metrics-core in shaded driver +- [improvement] JAVA-3115: SchemaChangeListener#onKeyspaceChanged can fire when keyspace has not changed if using SimpleStrategy replication + +## 3.11.4 +- [improvement] JAVA-3079: Upgrade Netty to 4.1.94, 3.x edition +- [improvement] JAVA-3082: Fix maven build for Apple-silicon +- [improvement] PR 1671: Fix LatencyAwarePolicy scale docstring + +## 3.11.3 + +- [improvement] JAVA-3023: Upgrade Netty to 4.1.77, 3.x edition + +## 3.11.2 + +- [improvement] JAVA-3008: Upgrade Netty to 4.1.75, 3.x edition +- [improvement] JAVA-2984: Upgrade Jackson to resolve high-priority CVEs + +## 3.11.1 + +- [bug] JAVA-2967: Support native transport peer information for DSE 6.8. +- [bug] JAVA-2976: Support missing protocol v5 error codes CAS_WRITE_UNKNOWN, CDC_WRITE_FAILURE. + +## 3.11.0 + +- [improvement] JAVA-2705: Remove protocol v5 beta status, add v6-beta. +- [bug] JAVA-2923: Detect and use Guava's new HostAndPort.getHost method. +- [bug] JAVA-2922: Switch to modern framing format inside a channel handler. +- [bug] JAVA-2924: Consider protocol version unsupported when server requires USE_BETA flag for it. + +### 3.10.2 + +- [bug] JAVA-2860: Avoid NPE if channel initialization crashes. + +### 3.10.1 + +- [bug] JAVA-2857: Fix NPE when built statements without parameters are logged at TRACE level. +- [bug] JAVA-2843: Successfully parse DSE table schema in OSS driver. + +### 3.10.0 + +- [improvement] JAVA-2676: Don't reschedule flusher after empty runs +- [new feature] JAVA-2772: Support new protocol v5 message format + +### 3.9.0 + +- [bug] JAVA-2627: Avoid logging error message including stack trace in request handler. +- [new feature] JAVA-2706: Add now_in_seconds to protocol v5 query messages. +- [improvement] JAVA-2730: Add support for Cassandra® 4.0 table options +- [improvement] JAVA-2702: Transient Replication Support for Cassandra® 4.0 + +### 3.8.0 + +- [new feature] JAVA-2356: Support for DataStax Cloud API. +- [improvement] JAVA-2483: Allow to provide secure bundle via URL. +- [improvement] JAVA-2499: Allow to read the secure bundle from an InputStream. +- [improvement] JAVA-2457: Detect CaaS and change default consistency. +- [improvement] JAVA-2485: Add errors for Cloud misconfiguration. +- [documentation] JAVA-2504: Migrate Cloud "getting started" page to driver manual. +- [improvement] JAVA-2516: Enable hostname validation with Cloud +- [bug] JAVA-2515: NEW_NODE and REMOVED_NODE events should trigger ADDED and REMOVED. + + +### 3.7.2 + +- [bug] JAVA-2249: Stop stripping trailing zeros in ByteOrderedTokens. +- [bug] JAVA-1492: Don't immediately reuse busy connections for another request. +- [bug] JAVA-2198: Handle UDTs with names that clash with collection types. +- [bug] JAVA-2204: Avoid memory leak when client holds onto a stale TableMetadata instance. + + +### 3.7.1 + +- [bug] JAVA-2174: Metadata.needsQuote should accept empty strings. +- [bug] JAVA-2193: Fix flaky tests in WarningsTest. + + +### 3.7.0 + +- [improvement] JAVA-2025: Include exception message in Abstract\*Codec.accepts(null). +- [improvement] JAVA-1980: Use covariant return types in RemoteEndpointAwareJdkSSLOptions.Builder methods. +- [documentation] JAVA-2062: Document frozen collection preference with Mapper. +- [bug] JAVA-2071: Fix NPE in ArrayBackedRow.toString(). +- [bug] JAVA-2070: Call onRemove instead of onDown when rack and/or DC information changes for a host. +- [improvement] JAVA-1256: Log parameters of BuiltStatement in QueryLogger. +- [documentation] JAVA-2074: Document preference for LZ4 over Snappy. +- [bug] JAVA-1612: Include netty-common jar in binary tarball. +- [improvement] JAVA-2003: Simplify CBUtil internal API to improve performance. +- [improvement] JAVA-2002: Reimplement TypeCodec.accepts to improve performance. +- [documentation] JAVA-2041: Deprecate cross-DC failover in DCAwareRoundRobinPolicy. +- [documentation] JAVA-1159: Document workaround for using tuple with udt field in Mapper. +- [documentation] JAVA-1964: Complete remaining "Coming Soon" sections in docs. +- [improvement] JAVA-1950: Log server side warnings returned from a query. +- [improvement] JAVA-2123: Allow to use QueryBuilder for building queries against Materialized Views. +- [bug] JAVA-2082: Avoid race condition during cluster close and schema refresh. + + +### 3.6.0 + +- [improvement] JAVA-1394: Add request-queue-depth metric. +- [improvement] JAVA-1857: Add Statement.setHost. +- [bug] JAVA-1920: Use nanosecond precision in LocalTimeCodec#format(). +- [bug] JAVA-1794: Driver tries to create a connection array of size -1. +- [new feature] JAVA-1899: Support virtual tables. +- [bug] JAVA-1908: TableMetadata.asCQLQuery does not add table option 'memtable_flush_period_in_ms' in the generated query. +- [bug] JAVA-1924: StatementWrapper setters should return the wrapping statement. +- [new feature] JAVA-1532: Add Codec support for Java 8's LocalDateTime and ZoneId. +- [improvement] JAVA-1786: Use Google code formatter. +- [bug] JAVA-1871: Change LOCAL\_SERIAL.isDCLocal() to return true. +- [documentation] JAVA-1902: Clarify unavailable & request error in DefaultRetryPolicy javadoc. +- [new feature] JAVA-1903: Add WhiteListPolicy.ofHosts. +- [bug] JAVA-1928: Fix GuavaCompatibility for Guava 26. +- [bug] JAVA-1935: Add null check in QueryConsistencyException.getHost. +- [improvement] JAVA-1771: Send driver name and version in STARTUP message. +- [improvement] JAVA-1388: Add dynamic port discovery for system.peers\_v2. +- [documentation] JAVA-1810: Note which setters are not propagated to PreparedStatement. +- [bug] JAVA-1944: Surface Read and WriteFailureException to RetryPolicy. +- [bug] JAVA-1211: Fix NPE in cluster close when cluster init fails. +- [bug] JAVA-1220: Fail fast on cluster init if previous init failed. +- [bug] JAVA-1929: Preempt session execute queries if session was closed. + +Merged from 3.5.x: + +- [bug] JAVA-1872: Retain table's views when processing table update. + + +### 3.5.0 + +- [improvement] JAVA-1448: TokenAwarePolicy should respect child policy ordering. +- [bug] JAVA-1751: Include defaultTimestamp length in encodedSize for protocol version >= 3. +- [bug] JAVA-1770: Fix message size when using Custom Payload. +- [documentation] JAVA-1760: Add metrics documentation. +- [improvement] JAVA-1765: Update dependencies to latest patch versions. +- [improvement] JAVA-1752: Deprecate DowngradingConsistencyRetryPolicy. +- [improvement] JAVA-1735: Log driver version on first use. +- [documentation] JAVA-1380: Add FAQ entry for errors arising from incompatibilities. +- [improvement] JAVA-1748: Support IS NOT NULL and != in query builder. +- [documentation] JAVA-1740: Mention C*2.2/3.0 incompatibilities in paging state manual. +- [improvement] JAVA-1725: Add a getNodeCount method to CCMAccess for easier automation. +- [new feature] JAVA-708: Add means to measure request sizes. +- [documentation] JAVA-1788: Add example for enabling host name verification to SSL docs. +- [improvement] JAVA-1791: Revert "JAVA-1677: Warn if auth is configured on the client but not the server." +- [bug] JAVA-1789: Account for flags in Prepare encodedSize. +- [bug] JAVA-1797: Use jnr-ffi version required by jnr-posix. + + +### 3.4.0 + +- [improvement] JAVA-1671: Remove unnecessary test on prepared statement metadata. +- [bug] JAVA-1694: Upgrade to jackson-databind 2.7.9.2 to address CVE-2015-15095. +- [documentation] JAVA-1685: Clarify recommendation on preparing SELECT *. +- [improvement] JAVA-1679: Improve error message on batch log write timeout. +- [improvement] JAVA-1672: Remove schema agreement check when repreparing on up. +- [improvement] JAVA-1677: Warn if auth is configured on the client but not the server. +- [new feature] JAVA-1651: Add NO_COMPACT startup option. +- [improvement] JAVA-1683: Add metrics to track writes to nodes. +- [new feature] JAVA-1229: Allow specifying the keyspace for individual queries. +- [improvement] JAVA-1682: Provide a way to record latencies for cancelled speculative executions. +- [improvement] JAVA-1717: Add metrics to latency-aware policy. +- [improvement] JAVA-1675: Remove dates from copyright headers. + +Merged from 3.3.x: + +- [bug] JAVA-1555: Include VIEW and CDC in WriteType. +- [bug] JAVA-1599: exportAsString improvements (sort, format, clustering order) +- [improvement] JAVA-1587: Deterministic ordering of columns used in Mapper#saveQuery +- [improvement] JAVA-1500: Add a metric to report number of in-flight requests. +- [bug] JAVA-1438: QueryBuilder check for empty orderings. +- [improvement] JAVA-1490: Allow zero delay for speculative executions. +- [documentation] JAVA-1607: Add FAQ entry for netty-transport-native-epoll. +- [bug] JAVA-1630: Fix Metadata.addIfAbsent. +- [improvement] JAVA-1619: Update QueryBuilder methods to support Iterable input. +- [improvement] JAVA-1527: Expose host_id and schema_version on Host metadata. +- [new feature] JAVA-1377: Add support for TWCS in SchemaBuilder. +- [improvement] JAVA-1631: Publish a sources jar for driver-core-tests. +- [improvement] JAVA-1632: Add a withIpPrefix(String) method to CCMBridge.Builder. +- [bug] JAVA-1639: VersionNumber does not fullfill equals/hashcode contract. +- [bug] JAVA-1613: Fix broken shaded Netty detection in NettyUtil. +- [bug] JAVA-1666: Fix keyspace export when a UDT has case-sensitive field names. +- [improvement] JAVA-1196: Include hash of result set metadata in prepared statement id. +- [improvement] JAVA-1670: Support user-provided JMX ports for CCMBridge. +- [improvement] JAVA-1661: Avoid String.toLowerCase if possible in Metadata. +- [improvement] JAVA-1659: Expose low-level flusher tuning options. +- [improvement] JAVA-1660: Support netty-transport-native-epoll in OSGi container. + + +### 3.3.2 + +- [bug] JAVA-1666: Fix keyspace export when a UDT has case-sensitive field names. +- [improvement] JAVA-1196: Include hash of result set metadata in prepared statement id. +- [improvement] JAVA-1670: Support user-provided JMX ports for CCMBridge. +- [improvement] JAVA-1661: Avoid String.toLowerCase if possible in Metadata. +- [improvement] JAVA-1659: Expose low-level flusher tuning options. +- [improvement] JAVA-1660: Support netty-transport-native-epoll in OSGi container. + + +### 3.3.1 + +- [bug] JAVA-1555: Include VIEW and CDC in WriteType. +- [bug] JAVA-1599: exportAsString improvements (sort, format, clustering order) +- [improvement] JAVA-1587: Deterministic ordering of columns used in Mapper#saveQuery +- [improvement] JAVA-1500: Add a metric to report number of in-flight requests. +- [bug] JAVA-1438: QueryBuilder check for empty orderings. +- [improvement] JAVA-1490: Allow zero delay for speculative executions. +- [documentation] JAVA-1607: Add FAQ entry for netty-transport-native-epoll. +- [bug] JAVA-1630: Fix Metadata.addIfAbsent. +- [improvement] JAVA-1619: Update QueryBuilder methods to support Iterable input. +- [improvement] JAVA-1527: Expose host_id and schema_version on Host metadata. +- [new feature] JAVA-1377: Add support for TWCS in SchemaBuilder. +- [improvement] JAVA-1631: Publish a sources jar for driver-core-tests. +- [improvement] JAVA-1632: Add a withIpPrefix(String) method to CCMBridge.Builder. +- [bug] JAVA-1639: VersionNumber does not fullfill equals/hashcode contract. +- [bug] JAVA-1613: Fix broken shaded Netty detection in NettyUtil. + + +### 3.3.0 + +- [bug] JAVA-1469: Update LoggingRetryPolicy to deal with SLF4J-353. +- [improvement] JAVA-1203: Upgrade Metrics to allow usage in OSGi. +- [bug] JAVA-1407: KeyspaceMetadata exportAsString should export user types in topological sort order. +- [bug] JAVA-1455: Mapper support using unset for null values. +- [bug] JAVA-1464: Allow custom codecs with non public constructors in @Param. +- [bug] JAVA-1470: Querying multiple pages overrides WrappedStatement. +- [improvement] JAVA-1428: Upgrade logback and jackson dependencies. +- [documentation] JAVA-1463: Revisit speculative execution docs. +- [documentation] JAVA-1466: Revisit timestamp docs. +- [documentation] JAVA-1445: Clarify how nodes are penalized in LatencyAwarePolicy docs. +- [improvement] JAVA-1446: Support 'DEFAULT UNSET' in Query Builder JSON Insert. +- [improvement] JAVA-1443: Add groupBy method to Select statement. +- [improvement] JAVA-1458: Check thread in mapper sync methods. +- [improvement] JAVA-1488: Upgrade Netty to 4.0.47.Final. +- [improvement] JAVA-1460: Add speculative execution number to ExecutionInfo +- [improvement] JAVA-1431: Improve error handling during pool initialization. + + +### 3.2.0 + +- [new feature] JAVA-1347: Add support for duration type. +- [new feature] JAVA-1248: Implement "beta" flag for native protocol v5. +- [new feature] JAVA-1362: Send query options flags as [int] for Protocol V5+. +- [new feature] JAVA-1364: Enable creation of SSLHandler with remote address information. +- [improvement] JAVA-1367: Make protocol negotiation more resilient. +- [bug] JAVA-1397: Handle duration as native datatype in protocol v5+. +- [improvement] JAVA-1308: CodecRegistry performance improvements. +- [improvement] JAVA-1287: Add CDC to TableOptionsMetadata and Schema Builder. +- [improvement] JAVA-1392: Reduce lock contention in RPTokenFactory. +- [improvement] JAVA-1328: Provide compatibility with Guava 20. +- [improvement] JAVA-1247: Disable idempotence warnings. +- [improvement] JAVA-1286: Support setting and retrieving udt fields in QueryBuilder. +- [bug] JAVA-1415: Correctly report if a UDT column is frozen. +- [bug] JAVA-1418: Make Guava version detection more reliable. +- [new feature] JAVA-1174: Add ifNotExists option to mapper. +- [improvement] JAVA-1414: Optimize Metadata.escapeId and Metadata.handleId. +- [improvement] JAVA-1310: Make mapper's ignored properties configurable. +- [improvement] JAVA-1316: Add strategy for resolving properties into CQL names. +- [bug] JAVA-1424: Handle new WRITE_FAILURE and READ_FAILURE format in v5 protocol. + +Merged from 3.1.x branch: + +- [bug] JAVA-1371: Reintroduce connection pool timeout. +- [bug] JAVA-1313: Copy SerialConsistencyLevel to PreparedStatement. +- [documentation] JAVA-1334: Clarify documentation of method `addContactPoints`. +- [improvement] JAVA-1357: Document that getReplicas only returns replicas of the last token in range. +- [bug] JAVA-1404: Fix min token handling in TokenRange.contains. +- [bug] JAVA-1429: Prevent heartbeats until connection is fully initialized. + + +### 3.1.4 + +Merged from 3.0.x branch: + +- [bug] JAVA-1371: Reintroduce connection pool timeout. +- [bug] JAVA-1313: Copy SerialConsistencyLevel to PreparedStatement. +- [documentation] JAVA-1334: Clarify documentation of method `addContactPoints`. +- [improvement] JAVA-1357: Document that getReplicas only returns replicas of the last token in range. + + +### 3.1.3 + +Merged from 3.0.x branch: + +- [bug] JAVA-1330: Add un/register for SchemaChangeListener in DelegatingCluster +- [bug] JAVA-1351: Include Custom Payload in Request.copy. +- [bug] JAVA-1346: Reset heartbeat only on client reads (not writes). +- [improvement] JAVA-866: Support tuple notation in QueryBuilder.eq/in. + + +### 3.1.2 + +- [bug] JAVA-1321: Wrong OSGi dependency version for Guava. + +Merged from 3.0.x branch: + +- [bug] JAVA-1312: QueryBuilder modifies selected columns when manually selected. +- [improvement] JAVA-1303: Add missing BoundStatement.setRoutingKey(ByteBuffer...) +- [improvement] JAVA-262: Make internal executors customizable + + +### 3.1.1 + +- [bug] JAVA-1284: ClockFactory should check system property before attempting to load Native class. +- [bug] JAVA-1255: Allow nested UDTs to be used in Mapper. +- [bug] JAVA-1279: Mapper should exclude Groovy's "metaClass" property when looking for mapped properties + +Merged from 3.0.x branch: + +- [improvement] JAVA-1246: Driver swallows the real exception in a few cases +- [improvement] JAVA-1261: Throw error when attempting to page in I/O thread. +- [bug] JAVA-1258: Regression: Mapper cannot map a materialized view after JAVA-1126. +- [bug] JAVA-1101: Batch and BatchStatement should consider inner statements to determine query idempotence +- [improvement] JAVA-1262: Use ParseUtils for quoting & unquoting. +- [improvement] JAVA-1275: Use Netty's default thread factory +- [bug] JAVA-1285: QueryBuilder routing key auto-discovery should handle case-sensitive column names. +- [bug] JAVA-1283: Don't cache failed query preparations in the mapper. +- [improvement] JAVA-1277: Expose AbstractSession.checkNotInEventLoop. +- [bug] JAVA-1272: BuiltStatement not able to print its query string if it contains mapped UDTs. +- [bug] JAVA-1292: 'Adjusted frame length' error breaks driver's ability to read data. +- [improvement] JAVA-1293: Make DecoderForStreamIdSize.MAX_FRAME_LENGTH configurable. +- [improvement] JAVA-1053: Add a metric for authentication errors +- [improvement] JAVA-1263: Eliminate unnecessary memory copies in FrameCompressor implementations. +- [improvement] JAVA-893: Make connection pool non-blocking + + +### 3.1.0 + +- [new feature] JAVA-1153: Add PER PARTITION LIMIT to Select QueryBuilder. +- [improvement] JAVA-743: Add JSON support to QueryBuilder. +- [improvement] JAVA-1233: Update HdrHistogram to 2.1.9. +- [improvement] JAVA-1233: Update Snappy to 1.1.2.6. +- [bug] JAVA-1161: Preserve full time zone info in ZonedDateTimeCodec and DateTimeCodec. +- [new feature] JAVA-1157: Allow asynchronous paging of Mapper Result. +- [improvement] JAVA-1212: Don't retry non-idempotent statements by default. +- [improvement] JAVA-1192: Make EventDebouncer settings updatable at runtime. +- [new feature] JAVA-541: Add polymorphism support to object mapper. +- [new feature] JAVA-636: Allow @Column annotations on getters/setters as well as fields. +- [new feature] JAVA-984: Allow non-void setters in object mapping. +- [new feature] JAVA-1055: Add ErrorAware load balancing policy. + +Merged from 3.0.x branch: + +- [bug] JAVA-1179: Request objects should be copied when executed. +- [improvement] JAVA-1182: Throw error when synchronous call made on I/O thread. +- [bug] JAVA-1184: Unwrap StatementWrappers when extracting column definitions. +- [bug] JAVA-1132: Executing bound statement with no variables results in exception with protocol v1. +- [improvement] JAVA-1040: SimpleStatement parameters support in QueryLogger. +- [improvement] JAVA-1151: Fail fast if HdrHistogram is not in the classpath. +- [improvement] JAVA-1154: Allow individual Statement to cancel the read timeout. +- [bug] JAVA-1074: Fix documentation around default timestamp generator. +- [improvement] JAVA-1109: Document SSLOptions changes in upgrade guide. +- [improvement] JAVA-1065: Add method to create token from partition key values. +- [improvement] JAVA-1136: Enable JDK signature check in module driver-extras. +- [improvement] JAVA-866: Support tuple notation in QueryBuilder.eq/in. +- [bug] JAVA-1140: Use same connection to check for schema agreement after a DDL query. +- [improvement] JAVA-1113: Support Cassandra 3.4 LIKE operator in QueryBuilder. +- [improvement] JAVA-1086: Support Cassandra 3.2 CAST function in QueryBuilder. +- [bug] JAVA-1095: Check protocol version for custom payload before sending the query. +- [improvement] JAVA-1133: Add OSGi headers to cassandra-driver-extras. +- [bug] JAVA-1137: Incorrect string returned by DataType.asFunctionParameterString() for collections and tuples. +- [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. +- [documentation] JAVA-1150: Add example and FAQ entry about ByteBuffer/BLOB. +- [improvement] JAVA-1011: Expose PoolingOptions default values. +- [improvement] JAVA-630: Don't process DOWN events for nodes that have active connections. +- [improvement] JAVA-851: Improve UUIDs javadoc with regard to user-provided timestamps. +- [improvement] JAVA-979: Update javadoc for RegularStatement toString() and getQueryString() to indicate that consistency level and other parameters are not maintained in the query string. +- [bug] JAVA-1068: Unwrap StatementWrappers when hashing the paging state. +- [improvement] JAVA-1021: Improve error message when connect() is called with an invalid keyspace name. +- [improvement] JAVA-879: Mapper.map() accepts mapper-generated and user queries. +- [bug] JAVA-1100: Exception when connecting with shaded java driver in OSGI +- [bug] JAVA-1064: getTable create statement doesn't properly handle quotes in primary key. +- [bug] JAVA-1089: Set LWT made from BuiltStatements to non-idempotent. +- [improvement] JAVA-923: Position idempotent flag on object mapper queries. +- [bug] JAVA-1070: The Mapper should not prepare queries synchronously. +- [new feature] JAVA-982: Introduce new method ConsistencyLevel.isSerial(). +- [bug] JAVA-764: Retry with the normal consistency level (not the serial one) when a write times out on the Paxos phase. +- [improvement] JAVA-852: Ignore peers with null entries during discovery. +- [bug] JAVA-1005: DowngradingConsistencyRetryPolicy does not work with EACH_QUORUM when 1 DC is down. +- [bug] JAVA-1002: Avoid deadlock when re-preparing a statement on other hosts. +- [bug] JAVA-1072: Ensure defunct connections are properly evicted from the pool. +- [bug] JAVA-1152: Fix NPE at ControlConnection.refreshNodeListAndTokenMap(). + +Merged from 2.1 branch: + +- [improvement] JAVA-1038: Fetch node info by rpc_address if its broadcast_address is not in system.peers. +- [improvement] JAVA-888: Add cluster-wide percentile tracker. +- [improvement] JAVA-963: Automatically register PercentileTracker from components that use it. +- [new feature] JAVA-1019: SchemaBuilder support for CREATE/ALTER/DROP KEYSPACE. +- [bug] JAVA-727: Allow monotonic timestamp generators to drift in the future + use microsecond precision when possible. +- [improvement] JAVA-444: Add Java process information to UUIDs.makeNode() hash. + + +### 3.0.7 + +- [bug] JAVA-1371: Reintroduce connection pool timeout. +- [bug] JAVA-1313: Copy SerialConsistencyLevel to PreparedStatement. +- [documentation] JAVA-1334: Clarify documentation of method `addContactPoints`. +- [improvement] JAVA-1357: Document that getReplicas only returns replicas of the last token in range. + + +### 3.0.6 + +- [bug] JAVA-1330: Add un/register for SchemaChangeListener in DelegatingCluster +- [bug] JAVA-1351: Include Custom Payload in Request.copy. +- [bug] JAVA-1346: Reset heartbeat only on client reads (not writes). +- [improvement] JAVA-866: Support tuple notation in QueryBuilder.eq/in. + + +### 3.0.5 + +- [bug] JAVA-1312: QueryBuilder modifies selected columns when manually selected. +- [improvement] JAVA-1303: Add missing BoundStatement.setRoutingKey(ByteBuffer...) +- [improvement] JAVA-262: Make internal executors customizable +- [bug] JAVA-1320: prevent unnecessary task creation on empty pool + + +### 3.0.4 + +- [improvement] JAVA-1246: Driver swallows the real exception in a few cases +- [improvement] JAVA-1261: Throw error when attempting to page in I/O thread. +- [bug] JAVA-1258: Regression: Mapper cannot map a materialized view after JAVA-1126. +- [bug] JAVA-1101: Batch and BatchStatement should consider inner statements to determine query idempotence +- [improvement] JAVA-1262: Use ParseUtils for quoting & unquoting. +- [improvement] JAVA-1275: Use Netty's default thread factory +- [bug] JAVA-1285: QueryBuilder routing key auto-discovery should handle case-sensitive column names. +- [bug] JAVA-1283: Don't cache failed query preparations in the mapper. +- [improvement] JAVA-1277: Expose AbstractSession.checkNotInEventLoop. +- [bug] JAVA-1272: BuiltStatement not able to print its query string if it contains mapped UDTs. +- [bug] JAVA-1292: 'Adjusted frame length' error breaks driver's ability to read data. +- [improvement] JAVA-1293: Make DecoderForStreamIdSize.MAX_FRAME_LENGTH configurable. +- [improvement] JAVA-1053: Add a metric for authentication errors +- [improvement] JAVA-1263: Eliminate unnecessary memory copies in FrameCompressor implementations. +- [improvement] JAVA-893: Make connection pool non-blocking + + +### 3.0.3 + +- [improvement] JAVA-1147: Upgrade Netty to 4.0.37. +- [bug] JAVA-1213: Allow updates and inserts to BLOB column using read-only ByteBuffer. +- [bug] JAVA-1209: ProtocolOptions.getProtocolVersion() should return null instead of throwing NPE if Cluster has not + been init'd. +- [improvement] JAVA-1204: Update documentation to indicate tcnative version requirement. +- [bug] JAVA-1186: Fix duplicated hosts in DCAwarePolicy warn message. +- [bug] JAVA-1187: Fix warning message when local CL used with RoundRobinPolicy. +- [improvement] JAVA-1175: Warn if DCAwarePolicy configuration is inconsistent. +- [bug] JAVA-1139: ConnectionException.getMessage() throws NPE if address is null. +- [bug] JAVA-1202: Handle null rpc_address when checking schema agreement. +- [improvement] JAVA-1198: Document that BoundStatement is not thread-safe. +- [improvement] JAVA-1200: Upgrade LZ4 to 1.3.0. +- [bug] JAVA-1232: Fix NPE in IdempotenceAwareRetryPolicy.isIdempotent. +- [improvement] JAVA-1227: Document "SELECT *" issue with prepared statement. +- [bug] JAVA-1160: Fix NPE in VersionNumber.getPreReleaseLabels(). +- [improvement] JAVA-1126: Handle schema changes in Mapper. +- [bug] JAVA-1193: Refresh token and replica metadata synchronously when schema is altered. +- [bug] JAVA-1120: Skip schema refresh debouncer when checking for agreement as a result of schema change made by client. +- [improvement] JAVA-1242: Fix driver-core dependency in driver-stress +- [improvement] JAVA-1235: Move the query to the end of "re-preparing .." log message as a key value. + + +### 3.0.2 + +Merged from 2.1 branch: + +- [bug] JAVA-1179: Request objects should be copied when executed. +- [improvement] JAVA-1182: Throw error when synchronous call made on I/O thread. +- [bug] JAVA-1184: Unwrap StatementWrappers when extracting column definitions. + + +### 3.0.1 + +- [bug] JAVA-1132: Executing bound statement with no variables results in exception with protocol v1. +- [improvement] JAVA-1040: SimpleStatement parameters support in QueryLogger. +- [improvement] JAVA-1151: Fail fast if HdrHistogram is not in the classpath. +- [improvement] JAVA-1154: Allow individual Statement to cancel the read timeout. +- [bug] JAVA-1074: Fix documentation around default timestamp generator. +- [improvement] JAVA-1109: Document SSLOptions changes in upgrade guide. +- [improvement] JAVA-1065: Add method to create token from partition key values. +- [improvement] JAVA-1136: Enable JDK signature check in module driver-extras. +- [improvement] JAVA-866: Support tuple notation in QueryBuilder.eq/in. +- [bug] JAVA-1140: Use same connection to check for schema agreement after a DDL query. +- [improvement] JAVA-1113: Support Cassandra 3.4 LIKE operator in QueryBuilder. +- [improvement] JAVA-1086: Support Cassandra 3.2 CAST function in QueryBuilder. +- [bug] JAVA-1095: Check protocol version for custom payload before sending the query. +- [improvement] JAVA-1133: Add OSGi headers to cassandra-driver-extras. +- [bug] JAVA-1137: Incorrect string returned by DataType.asFunctionParameterString() for collections and tuples. +- [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. +- [documentation] JAVA-1150: Add example and FAQ entry about ByteBuffer/BLOB. + +Merged from 2.1 branch: + +- [improvement] JAVA-1011: Expose PoolingOptions default values. +- [improvement] JAVA-630: Don't process DOWN events for nodes that have active connections. +- [improvement] JAVA-851: Improve UUIDs javadoc with regard to user-provided timestamps. +- [improvement] JAVA-979: Update javadoc for RegularStatement toString() and getQueryString() to indicate that consistency level and other parameters are not maintained in the query string. +- [bug] JAVA-1068: Unwrap StatementWrappers when hashing the paging state. +- [improvement] JAVA-1021: Improve error message when connect() is called with an invalid keyspace name. +- [improvement] JAVA-879: Mapper.map() accepts mapper-generated and user queries. +- [bug] JAVA-1100: Exception when connecting with shaded java driver in OSGI +- [bug] JAVA-1064: getTable create statement doesn't properly handle quotes in primary key. +- [bug] JAVA-1089: Set LWT made from BuiltStatements to non-idempotent. +- [improvement] JAVA-923: Position idempotent flag on object mapper queries. +- [bug] JAVA-1070: The Mapper should not prepare queries synchronously. +- [new feature] JAVA-982: Introduce new method ConsistencyLevel.isSerial(). +- [bug] JAVA-764: Retry with the normal consistency level (not the serial one) when a write times out on the Paxos phase. +- [improvement] JAVA-852: Ignore peers with null entries during discovery. +- [bug] JAVA-1005: DowngradingConsistencyRetryPolicy does not work with EACH_QUORUM when 1 DC is down. +- [bug] JAVA-1002: Avoid deadlock when re-preparing a statement on other hosts. +- [bug] JAVA-1072: Ensure defunct connections are properly evicted from the pool. +- [bug] JAVA-1152: Fix NPE at ControlConnection.refreshNodeListAndTokenMap(). + + +### 3.0.0 + +- [bug] JAVA-1034: fix metadata parser for collections of custom types. +- [improvement] JAVA-1035: Expose host broadcast_address and listen_address if available. +- [new feature] JAVA-1037: Allow named parameters in simple statements. +- [improvement] JAVA-1033: Allow per-statement read timeout. +- [improvement] JAVA-1042: Include DSE version and workload in Host data. + +Merged from 2.1 branch: + +- [improvement] JAVA-1030: Log token to replica map computation times. +- [bug] JAVA-1039: Minor bugs in Event Debouncer. + + +### 3.0.0-rc1 + +- [bug] JAVA-890: fix mapper for case-sensitive UDT. + + +### 3.0.0-beta1 + +- [bug] JAVA-993: Support for "custom" types after CASSANDRA-10365. +- [bug] JAVA-999: Handle unset parameters in QueryLogger. +- [bug] JAVA-998: SchemaChangeListener not invoked for Functions or Aggregates having UDT arguments. +- [bug] JAVA-1009: use CL ONE to compute query plan when reconnecting + control connection. +- [improvement] JAVA-1003: Change default consistency level to LOCAL_ONE (amends JAVA-926). +- [improvement] JAVA-863: Idempotence propagation in prepared statements. +- [improvement] JAVA-996: Make CodecRegistry available to ProtocolDecoder. +- [bug] JAVA-819: Driver shouldn't retry on client timeout if statement is not idempotent. +- [improvement] JAVA-1007: Make SimpleStatement and QueryBuilder "detached" again. + +Merged from 2.1 branch: + +- [improvement] JAVA-989: Include keyspace name when invalid replication found when generating token map. +- [improvement] JAVA-664: Reduce heap consumption for TokenMap. +- [bug] JAVA-994: Don't call on(Up|Down|Add|Remove) methods if Cluster is closed/closing. + + +### 3.0.0-alpha5 + +- [improvement] JAVA-958: Make TableOrView.Order visible. +- [improvement] JAVA-968: Update metrics to the latest version. +- [improvement] JAVA-965: Improve error handling for when a non-type 1 UUID is given to bind() on a timeuuid column. +- [improvement] JAVA-885: Pass the authenticator name from the server to the auth provider. +- [improvement] JAVA-961: Raise an exception when an older version of guava (<16.01) is found. +- [bug] JAVA-972: TypeCodec.parse() implementations should be case insensitive when checking for keyword NULL. +- [bug] JAVA-971: Make type codecs invariant. +- [bug] JAVA-986: Update documentation links to reference 3.0. +- [improvement] JAVA-841: Refactor SSLOptions API. +- [improvement] JAVA-948: Don't limit cipher suites by default. +- [improvement] JAVA-917: Document SSL configuration. +- [improvement] JAVA-936: Adapt schema metadata parsing logic to new storage format of CQL types in C* 3.0. +- [new feature] JAVA-846: Provide custom codecs library as an extra module. +- [new feature] JAVA-742: Codec Support for JSON. +- [new feature] JAVA-606: Codec support for Java 8. +- [new feature] JAVA-565: Codec support for Java arrays. +- [new feature] JAVA-605: Codec support for Java enums. +- [bug] JAVA-884: Fix UDT mapper to process fields in the correct order. + +Merged from 2.1 branch: + +- [bug] JAVA-854: avoid early return in Cluster.init when a node doesn't support the protocol version. +- [bug] JAVA-978: Fix quoting issue that caused Mapper.getTableMetadata() to return null. +- [improvement] JAVA-920: Downgrade "error creating pool" message to WARN. +- [bug] JAVA-954: Don't trigger reconnection before initialization complete. +- [improvement] JAVA-914: Avoid rejected tasks at shutdown. +- [improvement] JAVA-921: Add SimpleStatement.getValuesCount(). +- [bug] JAVA-901: Move call to connection.release() out of cancelHandler. +- [bug] JAVA-960: Avoid race in control connection shutdown. +- [bug] JAVA-656: Fix NPE in ControlConnection.updateLocationInfo. +- [bug] JAVA-966: Count uninitialized connections in conviction policy. +- [improvement] JAVA-917: Document SSL configuration. +- [improvement] JAVA-652: Add DCAwareRoundRobinPolicy builder. +- [improvement] JAVA-808: Add generic filtering policy that can be used to exclude specific DCs. +- [bug] JAVA-988: Metadata.handleId should handle escaped double quotes. +- [bug] JAVA-983: QueryBuilder cannot handle collections containing function calls. + + +### 3.0.0-alpha4 + +- [improvement] JAVA-926: Change default consistency level to LOCAL_QUORUM. +- [bug] JAVA-942: Fix implementation of UserType.hashCode(). +- [improvement] JAVA-877: Don't delay UP/ADDED notifications if protocol version = V4. +- [improvement] JAVA-938: Parse 'extensions' column in table metadata. +- [bug] JAVA-900: Fix Configuration builder to allow disabled metrics. +- [new feature] JAVA-902: Prepare API for async query trace. +- [new feature] JAVA-930: Add BoundStatement#unset. +- [bug] JAVA-946: Make table metadata options class visible. +- [bug] JAVA-939: Add crcCheckChance to TableOptionsMetadata#equals/hashCode. +- [bug] JAVA-922: Make TypeCodec return mutable collections. +- [improvement] JAVA-932: Limit visibility of codec internals. +- [improvement] JAVA-934: Warn if a custom codec collides with an existing one. +- [improvement] JAVA-940: Allow typed getters/setters to target any CQL type. +- [bug] JAVA-950: Fix Cluster.connect with a case-sensitive keyspace. +- [bug] JAVA-953: Fix MaterializedViewMetadata when base table name is case sensitive. + + +### 3.0.0-alpha3 + +- [new feature] JAVA-571: Support new system tables in C* 3.0. +- [improvement] JAVA-919: Move crc_check_chance out of compressions options. + +Merged from 2.0 branch: + +- [improvement] JAVA-718: Log streamid at the trace level on sending request and receiving response. +- [bug] JAVA-796: Fix SpeculativeExecutionPolicy.init() and close() are never called. +- [improvement] JAVA-710: Suppress unnecessary warning at shutdown. +- [improvement] #340: Allow DNS name with multiple A-records as contact point. +- [bug] JAVA-794: Allow tracing across multiple result pages. +- [bug] JAVA-737: DowngradingConsistencyRetryPolicy ignores write timeouts. +- [bug] JAVA-736: Forbid bind marker in QueryBuilder add/append/prepend. +- [bug] JAVA-712: Prevent QueryBuilder.quote() from applying duplicate double quotes. +- [bug] JAVA-688: Prevent QueryBuilder from trying to serialize raw string. +- [bug] JAVA-679: Support bind marker in QueryBuilder DELETE's list index. +- [improvement] JAVA-475: Improve QueryBuilder API for SELECT DISTINCT. +- [improvement] JAVA-225: Create values() function for Insert builder using List. +- [improvement] JAVA-702: Warn when ReplicationStrategy encounters invalid + replication factors. +- [improvement] JAVA-662: Add PoolingOptions method to set both core and max + connections. +- [improvement] JAVA-766: Do not include epoll JAR in binary distribution. +- [improvement] JAVA-726: Optimize internal copies of Request objects. +- [bug] JAVA-815: Preserve tracing across retries. +- [improvement] JAVA-709: New RetryDecision.tryNextHost(). +- [bug] JAVA-733: Handle function calls and raw strings as non-idempotent in QueryBuilder. +- [improvement] JAVA-765: Provide API to retrieve values of a Parameterized SimpleStatement. +- [improvement] JAVA-827: implement UPDATE .. IF EXISTS in QueryBuilder. +- [improvement] JAVA-618: Randomize contact points list to prevent hotspots. +- [improvement] JAVA-720: Surface the coordinator used on query failure. +- [bug] JAVA-792: Handle contact points removed during init. +- [improvement] JAVA-719: Allow PlainTextAuthProvider to change its credentials at runtime. +- [new feature] JAVA-151: Make it possible to register for SchemaChange Events. +- [improvement] JAVA-861: Downgrade "Asked to rebuild table" log from ERROR to INFO level. +- [improvement] JAVA-797: Provide an option to prepare statements only on one node. +- [improvement] JAVA-658: Provide an option to not re-prepare all statements in onUp. +- [improvement] JAVA-853: Customizable creation of netty timer. +- [bug] JAVA-859: Avoid quadratic ring processing with invalid replication factors. +- [improvement] JAVA-657: Debounce control connection queries. +- [bug] JAVA-784: LoadBalancingPolicy.distance() called before init(). +- [new feature] JAVA-828: Make driver-side metadata optional. +- [improvement] JAVA-544: Allow hosts to remain partially up. +- [improvement] JAVA-821, JAVA-822: Remove internal blocking calls and expose async session + creation. +- [improvement] JAVA-725: Use parallel calls when re-preparing statement on other + hosts. +- [bug] JAVA-629: Don't use connection timeout for unrelated internal queries. +- [bug] JAVA-892: Fix NPE in speculative executions when metrics disabled. + + +### 3.0.0-alpha2 + +- [new feature] JAVA-875, JAVA-882: Move secondary index metadata out of column definitions. + +Merged from 2.2 branch: + +- [bug] JAVA-847: Propagate CodecRegistry to nested UDTs. +- [improvement] JAVA-848: Ability to store a default, shareable CodecRegistry + instance. +- [bug] JAVA-880: Treat empty ByteBuffers as empty values in TupleCodec and + UDTCodec. + + +### 3.0.0-alpha1 + +- [new feature] JAVA-876: Support new system tables in C* 3.0.0-alpha1. + +Merged from 2.2 branch: + +- [improvement] JAVA-810: Rename DateWithoutTime to LocalDate. +- [bug] JAVA-816: DateCodec does not format values correctly. +- [bug] JAVA-817: TimeCodec does not format values correctly. +- [bug] JAVA-818: TypeCodec.getDataTypeFor() does not handle LocalDate instances. +- [improvement] JAVA-836: Make ResultSet#fetchMoreResult return a + ListenableFuture. +- [improvement] JAVA-843: Disable frozen checks in mapper. +- [improvement] JAVA-721: Allow user to register custom type codecs. +- [improvement] JAVA-722: Support custom type codecs in mapper. + + +### 2.2.0-rc3 + +- [bug] JAVA-847: Propagate CodecRegistry to nested UDTs. +- [improvement] JAVA-848: Ability to store a default, shareable CodecRegistry + instance. +- [bug] JAVA-880: Treat empty ByteBuffers as empty values in TupleCodec and + UDTCodec. + + +### 2.2.0-rc2 + +- [improvement] JAVA-810: Rename DateWithoutTime to LocalDate. +- [bug] JAVA-816: DateCodec does not format values correctly. +- [bug] JAVA-817: TimeCodec does not format values correctly. +- [bug] JAVA-818: TypeCodec.getDataTypeFor() does not handle LocalDate instances. +- [improvement] JAVA-836: Make ResultSet#fetchMoreResult return a + ListenableFuture. +- [improvement] JAVA-843: Disable frozen checks in mapper. +- [improvement] JAVA-721: Allow user to register custom type codecs. +- [improvement] JAVA-722: Support custom type codecs in mapper. + +Merged from 2.1 branch: + +- [bug] JAVA-834: Special case check for 'null' string in index_options column. +- [improvement] JAVA-835: Allow accessor methods with less parameters in case + named bind markers are repeated. +- [improvement] JAVA-475: Improve QueryBuilder API for SELECT DISTINCT. +- [improvement] JAVA-715: Make NativeColumnType a top-level class. +- [improvement] JAVA-700: Expose ProtocolVersion#toInt. +- [bug] JAVA-542: Handle void return types in accessors. +- [improvement] JAVA-225: Create values() function for Insert builder using List. +- [improvement] JAVA-713: HashMap throws an OOM Exception when logging level is set to TRACE. +- [bug] JAVA-679: Support bind marker in QueryBuilder DELETE's list index. +- [improvement] JAVA-732: Expose KEYS and FULL indexing options in IndexMetadata. +- [improvement] JAVA-589: Allow @Enumerated in Accessor method parameters. +- [improvement] JAVA-554: Allow access to table metadata from Mapper. +- [improvement] JAVA-661: Provide a way to map computed fields. +- [improvement] JAVA-824: Ignore missing columns in mapper. +- [bug] JAVA-724: Preserve default timestamp for retries and speculative executions. +- [improvement] JAVA-738: Use same pool implementation for protocol v2 and v3. +- [improvement] JAVA-677: Support CONTAINS / CONTAINS KEY in QueryBuilder. +- [improvement] JAVA-477/JAVA-540: Add USING options in mapper for delete and save + operations. +- [improvement] JAVA-473: Add mapper option to configure whether to save null fields. + +Merged from 2.0 branch: + +- [bug] JAVA-737: DowngradingConsistencyRetryPolicy ignores write timeouts. +- [bug] JAVA-736: Forbid bind marker in QueryBuilder add/append/prepend. +- [bug] JAVA-712: Prevent QueryBuilder.quote() from applying duplicate double quotes. +- [bug] JAVA-688: Prevent QueryBuilder from trying to serialize raw string. +- [bug] JAVA-679: Support bind marker in QueryBuilder DELETE's list index. +- [improvement] JAVA-475: Improve QueryBuilder API for SELECT DISTINCT. +- [improvement] JAVA-225: Create values() function for Insert builder using List. +- [improvement] JAVA-702: Warn when ReplicationStrategy encounters invalid + replication factors. +- [improvement] JAVA-662: Add PoolingOptions method to set both core and max + connections. +- [improvement] JAVA-766: Do not include epoll JAR in binary distribution. +- [improvement] JAVA-726: Optimize internal copies of Request objects. +- [bug] JAVA-815: Preserve tracing across retries. +- [improvement] JAVA-709: New RetryDecision.tryNextHost(). +- [bug] JAVA-733: Handle function calls and raw strings as non-idempotent in QueryBuilder. + + +### 2.2.0-rc1 + +- [new feature] JAVA-783: Protocol V4 enum support. +- [new feature] JAVA-776: Use PK columns in protocol v4 PREPARED response. +- [new feature] JAVA-777: Distinguish NULL and UNSET values. +- [new feature] JAVA-779: Add k/v payload for 3rd party usage. +- [new feature] JAVA-780: Expose server-side warnings on ExecutionInfo. +- [new feature] JAVA-749: Expose new read/write failure exceptions. +- [new feature] JAVA-747: Expose function and aggregate metadata. +- [new feature] JAVA-778: Add new client exception for CQL function failure. +- [improvement] JAVA-700: Expose ProtocolVersion#toInt. +- [new feature] JAVA-404: Support new C* 2.2 CQL date and time types. + +Merged from 2.1 branch: + +- [improvement] JAVA-782: Unify "Target" enum for schema elements. + + +### 2.1.10.2 + +Merged from 2.0 branch: + +- [bug] JAVA-1179: Request objects should be copied when executed. +- [improvement] JAVA-1182: Throw error when synchronous call made on I/O thread. +- [bug] JAVA-1184: Unwrap StatementWrappers when extracting column definitions. + + +### 2.1.10.1 + +- [bug] JAVA-1152: Fix NPE at ControlConnection.refreshNodeListAndTokenMap(). +- [bug] JAVA-1156: Fix NPE at TableMetadata.equals(). + + +### 2.1.10 + +- [bug] JAVA-988: Metadata.handleId should handle escaped double quotes. +- [bug] JAVA-983: QueryBuilder cannot handle collections containing function calls. +- [improvement] JAVA-863: Idempotence propagation in PreparedStatements. +- [bug] JAVA-937: TypeCodec static initializers not always correctly executed. +- [improvement] JAVA-989: Include keyspace name when invalid replication found when generating token map. +- [improvement] JAVA-664: Reduce heap consumption for TokenMap. +- [improvement] JAVA-1030: Log token to replica map computation times. +- [bug] JAVA-1039: Minor bugs in Event Debouncer. +- [improvement] JAVA-843: Disable frozen checks in mapper. +- [improvement] JAVA-833: Improve message when a nested type can't be serialized. +- [improvement] JAVA-1011: Expose PoolingOptions default values. +- [improvement] JAVA-630: Don't process DOWN events for nodes that have active connections. +- [improvement] JAVA-851: Improve UUIDs javadoc with regard to user-provided timestamps. +- [improvement] JAVA-979: Update javadoc for RegularStatement toString() and getQueryString() to indicate that consistency level and other parameters are not maintained in the query string. +- [improvement] JAVA-1038: Fetch node info by rpc_address if its broadcast_address is not in system.peers. +- [improvement] JAVA-974: Validate accessor parameter types against bound statement. +- [bug] JAVA-1068: Unwrap StatementWrappers when hashing the paging state. +- [bug] JAVA-831: Mapper can't load an entity where the PK is a UDT. +- [improvement] JAVA-1021: Improve error message when connect() is called with an invalid keyspace name. +- [improvement] JAVA-879: Mapper.map() accepts mapper-generated and user queries. +- [bug] JAVA-1100: Exception when connecting with shaded java driver in OSGI +- [bug] JAVA-819: Expose more errors in RetryPolicy + provide idempotent-aware wrapper. +- [improvement] JAVA-1040: SimpleStatement parameters support in QueryLogger. +- [bug] JAVA-1064: getTable create statement doesn't properly handle quotes in primary key. +- [improvement] JAVA-888: Add cluster-wide percentile tracker. +- [improvement] JAVA-963: Automatically register PercentileTracker from components that use it. +- [bug] JAVA-1089: Set LWT made from BuiltStatements to non-idempotent. +- [improvement] JAVA-923: Position idempotent flag on object mapper queries. +- [new feature] JAVA-1019: SchemaBuilder support for CREATE/ALTER/DROP KEYSPACE. +- [bug] JAVA-1070: The Mapper should not prepare queries synchronously. +- [new feature] JAVA-982: Introduce new method ConsistencyLevel.isSerial(). +- [bug] JAVA-764: Retry with the normal consistency level (not the serial one) when a write times out on the Paxos phase. +- [bug] JAVA-727: Allow monotonic timestamp generators to drift in the future + use microsecond precision when possible. +- [improvement] JAVA-444: Add Java process information to UUIDs.makeNode() hash. +- [improvement] JAVA-977: Preserve original cause when BuiltStatement value can't be serialized. +- [bug] JAVA-1094: Backport TypeCodec parse and format fixes from 3.0. +- [improvement] JAVA-852: Ignore peers with null entries during discovery. +- [bug] JAVA-1132: Executing bound statement with no variables results in exception with protocol v1. +- [bug] JAVA-1005: DowngradingConsistencyRetryPolicy does not work with EACH_QUORUM when 1 DC is down. +- [bug] JAVA-1002: Avoid deadlock when re-preparing a statement on other hosts. + +Merged from 2.0 branch: + +- [bug] JAVA-994: Don't call on(Up|Down|Add|Remove) methods if Cluster is closed/closing. +- [improvement] JAVA-805: Document that metrics are null until Cluster is initialized. +- [bug] JAVA-1072: Ensure defunct connections are properly evicted from the pool. + + +### 2.1.9 + +- [bug] JAVA-942: Fix implementation of UserType.hashCode(). +- [bug] JAVA-854: avoid early return in Cluster.init when a node doesn't support the protocol version. +- [bug] JAVA-978: Fix quoting issue that caused Mapper.getTableMetadata() to return null. + +Merged from 2.0 branch: + +- [bug] JAVA-950: Fix Cluster.connect with a case-sensitive keyspace. +- [improvement] JAVA-920: Downgrade "error creating pool" message to WARN. +- [bug] JAVA-954: Don't trigger reconnection before initialization complete. +- [improvement] JAVA-914: Avoid rejected tasks at shutdown. +- [improvement] JAVA-921: Add SimpleStatement.getValuesCount(). +- [bug] JAVA-901: Move call to connection.release() out of cancelHandler. +- [bug] JAVA-960: Avoid race in control connection shutdown. +- [bug] JAVA-656: Fix NPE in ControlConnection.updateLocationInfo. +- [bug] JAVA-966: Count uninitialized connections in conviction policy. +- [improvement] JAVA-917: Document SSL configuration. +- [improvement] JAVA-652: Add DCAwareRoundRobinPolicy builder. +- [improvement] JAVA-808: Add generic filtering policy that can be used to exclude specific DCs. + + +### 2.1.8 + +Merged from 2.0 branch: + +- [improvement] JAVA-718: Log streamid at the trace level on sending request and receiving response. + +- [bug] JAVA-796: Fix SpeculativeExecutionPolicy.init() and close() are never called. +- [improvement] JAVA-710: Suppress unnecessary warning at shutdown. +- [improvement] #340: Allow DNS name with multiple A-records as contact point. +- [bug] JAVA-794: Allow tracing across multiple result pages. +- [bug] JAVA-737: DowngradingConsistencyRetryPolicy ignores write timeouts. +- [bug] JAVA-736: Forbid bind marker in QueryBuilder add/append/prepend. +- [bug] JAVA-712: Prevent QueryBuilder.quote() from applying duplicate double quotes. +- [bug] JAVA-688: Prevent QueryBuilder from trying to serialize raw string. +- [bug] JAVA-679: Support bind marker in QueryBuilder DELETE's list index. +- [improvement] JAVA-475: Improve QueryBuilder API for SELECT DISTINCT. +- [improvement] JAVA-225: Create values() function for Insert builder using List. +- [improvement] JAVA-702: Warn when ReplicationStrategy encounters invalid + replication factors. +- [improvement] JAVA-662: Add PoolingOptions method to set both core and max + connections. +- [improvement] JAVA-766: Do not include epoll JAR in binary distribution. +- [improvement] JAVA-726: Optimize internal copies of Request objects. +- [bug] JAVA-815: Preserve tracing across retries. +- [improvement] JAVA-709: New RetryDecision.tryNextHost(). +- [bug] JAVA-733: Handle function calls and raw strings as non-idempotent in QueryBuilder. +- [improvement] JAVA-765: Provide API to retrieve values of a Parameterized SimpleStatement. +- [improvement] JAVA-827: implement UPDATE .. IF EXISTS in QueryBuilder. +- [improvement] JAVA-618: Randomize contact points list to prevent hotspots. +- [improvement] JAVA-720: Surface the coordinator used on query failure. +- [bug] JAVA-792: Handle contact points removed during init. +- [improvement] JAVA-719: Allow PlainTextAuthProvider to change its credentials at runtime. +- [new feature] JAVA-151: Make it possible to register for SchemaChange Events. +- [improvement] JAVA-861: Downgrade "Asked to rebuild table" log from ERROR to INFO level. +- [improvement] JAVA-797: Provide an option to prepare statements only on one node. +- [improvement] JAVA-658: Provide an option to not re-prepare all statements in onUp. +- [improvement] JAVA-853: Customizable creation of netty timer. +- [bug] JAVA-859: Avoid quadratic ring processing with invalid replication factors. +- [improvement] JAVA-657: Debounce control connection queries. +- [bug] JAVA-784: LoadBalancingPolicy.distance() called before init(). +- [new feature] JAVA-828: Make driver-side metadata optional. +- [improvement] JAVA-544: Allow hosts to remain partially up. +- [improvement] JAVA-821, JAVA-822: Remove internal blocking calls and expose async session + creation. +- [improvement] JAVA-725: Use parallel calls when re-preparing statement on other + hosts. +- [bug] JAVA-629: Don't use connection timeout for unrelated internal queries. +- [bug] JAVA-892: Fix NPE in speculative executions when metrics disabled. + + +### 2.1.7.1 + +- [bug] JAVA-834: Special case check for 'null' string in index_options column. +- [improvement] JAVA-835: Allow accessor methods with less parameters in case + named bind markers are repeated. + + +### 2.1.7 + +- [improvement] JAVA-475: Improve QueryBuilder API for SELECT DISTINCT. +- [improvement] JAVA-715: Make NativeColumnType a top-level class. +- [improvement] JAVA-782: Unify "Target" enum for schema elements. +- [improvement] JAVA-700: Expose ProtocolVersion#toInt. +- [bug] JAVA-542: Handle void return types in accessors. +- [improvement] JAVA-225: Create values() function for Insert builder using List. +- [improvement] JAVA-713: HashMap throws an OOM Exception when logging level is set to TRACE. +- [bug] JAVA-679: Support bind marker in QueryBuilder DELETE's list index. +- [improvement] JAVA-732: Expose KEYS and FULL indexing options in IndexMetadata. +- [improvement] JAVA-589: Allow @Enumerated in Accessor method parameters. +- [improvement] JAVA-554: Allow access to table metadata from Mapper. +- [improvement] JAVA-661: Provide a way to map computed fields. +- [improvement] JAVA-824: Ignore missing columns in mapper. +- [bug] JAVA-724: Preserve default timestamp for retries and speculative executions. +- [improvement] JAVA-738: Use same pool implementation for protocol v2 and v3. +- [improvement] JAVA-677: Support CONTAINS / CONTAINS KEY in QueryBuilder. +- [improvement] JAVA-477/JAVA-540: Add USING options in mapper for delete and save + operations. +- [improvement] JAVA-473: Add mapper option to configure whether to save null fields. + +Merged from 2.0 branch: + +- [bug] JAVA-737: DowngradingConsistencyRetryPolicy ignores write timeouts. +- [bug] JAVA-736: Forbid bind marker in QueryBuilder add/append/prepend. +- [bug] JAVA-712: Prevent QueryBuilder.quote() from applying duplicate double quotes. +- [bug] JAVA-688: Prevent QueryBuilder from trying to serialize raw string. +- [bug] JAVA-679: Support bind marker in QueryBuilder DELETE's list index. +- [improvement] JAVA-475: Improve QueryBuilder API for SELECT DISTINCT. +- [improvement] JAVA-225: Create values() function for Insert builder using List. +- [improvement] JAVA-702: Warn when ReplicationStrategy encounters invalid + replication factors. +- [improvement] JAVA-662: Add PoolingOptions method to set both core and max + connections. +- [improvement] JAVA-766: Do not include epoll JAR in binary distribution. +- [improvement] JAVA-726: Optimize internal copies of Request objects. +- [bug] JAVA-815: Preserve tracing across retries. +- [improvement] JAVA-709: New RetryDecision.tryNextHost(). +- [bug] JAVA-733: Handle function calls and raw strings as non-idempotent in QueryBuilder. + + +### 2.1.6 + +Merged from 2.0 branch: + +- [new feature] JAVA-584: Add getObject to BoundStatement and Row. +- [improvement] JAVA-419: Improve connection pool resizing algorithm. +- [bug] JAVA-599: Fix race condition between pool expansion and shutdown. +- [improvement] JAVA-622: Upgrade Netty to 4.0.27. +- [improvement] JAVA-562: Coalesce frames before flushing them to the connection. +- [improvement] JAVA-583: Rename threads to indicate that they are for the driver. +- [new feature] JAVA-550: Expose paging state. +- [new feature] JAVA-646: Slow Query Logger. +- [improvement] JAVA-698: Exclude some errors from measurements in LatencyAwarePolicy. +- [bug] JAVA-641: Fix issue when executing a PreparedStatement from another cluster. +- [improvement] JAVA-534: Log keyspace xxx does not exist at WARN level. +- [improvement] JAVA-619: Allow Cluster subclasses to delegate to another instance. +- [new feature] JAVA-669: Expose an API to check for schema agreement after a + schema-altering statement. +- [improvement] JAVA-692: Make connection and pool creation fully async. +- [improvement] JAVA-505: Optimize connection use after reconnection. +- [improvement] JAVA-617: Remove "suspected" mechanism. +- [improvement] reverts JAVA-425: Don't mark connection defunct on client timeout. +- [new feature] JAVA-561: Speculative query executions. +- [bug] JAVA-666: Release connection before completing the ResultSetFuture. +- [new feature BETA] JAVA-723: Percentile-based variant of query logger and speculative + executions. +- [bug] JAVA-734: Fix buffer leaks when compression is enabled. +- [improvement] JAVA-756: Use Netty's pooled ByteBufAllocator by default. +- [improvement] JAVA-759: Expose "unsafe" paging state API. +- [bug] JAVA-768: Prevent race during pool initialization. + + +### 2.1.5 + +- [bug] JAVA-575: Authorize Null parameter in Accessor method. +- [improvement] JAVA-570: Support C* 2.1.3's nested collections. +- [bug] JAVA-612: Fix checks on mapped collection types. +- [bug] JAVA-672: Fix QueryBuilder.putAll() when the collection contains UDTs. + +Merged from 2.0 branch: + +- [new feature] JAVA-518: Add AddressTranslater for EC2 multi-region deployment. +- [improvement] JAVA-533: Add connection heartbeat. +- [improvement] JAVA-568: Reduce level of logs on missing rpc_address. +- [improvement] JAVA-312, JAVA-681: Expose node token and range information. +- [bug] JAVA-595: Fix cluster name mismatch check at startup. +- [bug] JAVA-620: Fix guava dependency when using OSGI. +- [bug] JAVA-678: Fix handling of DROP events when ks name is case-sensitive. +- [improvement] JAVA-631: Use List instead of List in QueryBuilder API. +- [improvement] JAVA-654: Exclude Netty POM from META-INF in shaded JAR. +- [bug] JAVA-655: Quote single quotes contained in table comments in asCQLQuery method. +- [bug] JAVA-684: Empty TokenRange returned in a one token cluster. +- [improvement] JAVA-687: Expose TokenRange#contains. +- [bug] JAVA-614: Prevent race between cancellation and query completion. +- [bug] JAVA-632: Prevent cancel and timeout from cancelling unrelated ResponseHandler if + streamId was already released and reused. +- [bug] JAVA-642: Fix issue when newly opened pool fails before we could mark the node UP. +- [bug] JAVA-613: Fix unwanted LBP notifications when a contact host is down. +- [bug] JAVA-651: Fix edge cases where a connection was released twice. +- [bug] JAVA-653: Fix edge cases in query cancellation. + + +### 2.1.4 + +Merged from 2.0 branch: + +- [improvement] JAVA-538: Shade Netty dependency. +- [improvement] JAVA-543: Target schema refreshes more precisely. +- [bug] JAVA-546: Don't check rpc_address for control host. +- [improvement] JAVA-409: Improve message of NoHostAvailableException. +- [bug] JAVA-556: Rework connection reaper to avoid deadlock. +- [bug] JAVA-557: Avoid deadlock when multiple connections to the same host get write + errors. +- [improvement] JAVA-504: Make shuffle=true the default for TokenAwarePolicy. +- [bug] JAVA-577: Fix bug when SUSPECT reconnection succeeds, but one of the pooled + connections fails while bringing the node back up. +- [bug] JAVA-419: JAVA-587: Prevent faulty control connection from ignoring reconnecting hosts. +- temporarily revert "Add idle timeout to the connection pool". +- [bug] JAVA-593: Ensure updateCreatedPools does not add pools for suspected hosts. +- [bug] JAVA-594: Ensure state change notifications for a given host are handled serially. +- [bug] JAVA-597: Ensure control connection reconnects when control host is removed. + + +### 2.1.3 + +- [bug] JAVA-510: Ignore static fields in mapper. +- [bug] JAVA-509: Fix UDT parsing at init when using the default protocol version. +- [bug] JAVA-495: Fix toString, equals and hashCode on accessor proxies. +- [bug] JAVA-528: Allow empty name on Column and Field annotations. + +Merged from 2.0 branch: + +- [bug] JAVA-497: Ensure control connection does not trigger concurrent reconnects. +- [improvement] JAVA-472: Keep trying to reconnect on authentication errors. +- [improvement] JAVA-463: Expose close method on load balancing policy. +- [improvement] JAVA-459: Allow load balancing policy to trigger refresh for a single host. +- [bug] JAVA-493: Expose an API to cancel reconnection attempts. +- [bug] JAVA-503: Fix NPE when a connection fails during pool construction. +- [improvement] JAVA-423: Log datacenter name in DCAware policy's init when it is explicitly provided. +- [improvement] JAVA-504: Shuffle the replicas in TokenAwarePolicy.newQueryPlan. +- [improvement] JAVA-507: Make schema agreement wait tuneable. +- [improvement] JAVA-494: Document how to inject the driver metrics into another registry. +- [improvement] JAVA-419: Add idle timeout to the connection pool. +- [bug] JAVA-516: LatencyAwarePolicy does not shutdown executor on invocation of close. +- [improvement] JAVA-451: Throw an exception when DCAwareRoundRobinPolicy is built with + an explicit but null or empty local datacenter. +- [bug] JAVA-511: Fix check for local contact points in DCAware policy's init. +- [improvement] JAVA-457: Make timeout on saturated pool customizable. +- [improvement] JAVA-521: Downgrade Guava to 14.0.1. +- [bug] JAVA-526: Fix token awareness for case-sensitive keyspaces and tables. +- [bug] JAVA-515: Check maximum number of values passed to SimpleStatement. +- [improvement] JAVA-532: Expose the driver version through the API. +- [improvement] JAVA-522: Optimize session initialization when some hosts are not + responsive. + + +### 2.1.2 + +- [improvement] JAVA-361, JAVA-364, JAVA-467: Support for native protocol v3. +- [bug] JAVA-454: Fix UDT fields of type inet in QueryBuilder. +- [bug] JAVA-455: Exclude transient fields from Frozen checks. +- [bug] JAVA-453: Fix handling of null collections in mapper. +- [improvement] JAVA-452: Make implicit column names case-insensitive in mapper. +- [bug] JAVA-433: Fix named bind markers in QueryBuilder. +- [bug] JAVA-458: Fix handling of BigInteger in object mapper. +- [bug] JAVA-465: Ignore synthetic fields in mapper. +- [improvement] JAVA-451: Throw an exception when DCAwareRoundRobinPolicy is built with + an explicit but null or empty local datacenter. +- [improvement] JAVA-469: Add backwards-compatible DataType.serialize methods. +- [bug] JAVA-487: Handle null enum fields in object mapper. +- [bug] JAVA-499: Handle null UDT fields in object mapper. + +Merged from 2.0 branch: + +- [bug] JAVA-449: Handle null pool in PooledConnection.release. +- [improvement] JAVA-425: Defunct connection on request timeout. +- [improvement] JAVA-426: Try next host when we get a SERVER_ERROR. +- [bug] JAVA-449, JAVA-460, JAVA-471: Handle race between query timeout and completion. +- [bug] JAVA-496: Fix DCAwareRoundRobinPolicy datacenter auto-discovery. + + +### 2.1.1 + +- [new] JAVA-441: Support for new "frozen" keyword. + +Merged from 2.0 branch: + +- [bug] JAVA-397: Check cluster name when connecting to a new node. +- [bug] JAVA-326: Add missing CAS delete support in QueryBuilder. +- [bug] JAVA-363: Add collection and data length checks during serialization. +- [improvement] JAVA-329: Surface number of retries in metrics. +- [bug] JAVA-428: Do not use a host when no rpc_address found for it. +- [improvement] JAVA-358: Add ResultSet.wasApplied() for conditional queries. +- [bug] JAVA-349: Fix negative HostConnectionPool open count. +- [improvement] JAVA-436: Log more connection details at trace and debug levels. +- [bug] JAVA-445: Fix cluster shutdown. + + +### 2.1.0 + +- [bug] JAVA-408: ClusteringColumn annotation not working with specified ordering. +- [improvement] JAVA-410: Fail BoundStatement if null values are not set explicitly. +- [bug] JAVA-416: Handle UDT and tuples in BuiltStatement.toString. + +Merged from 2.0 branch: + +- [bug] JAVA-407: Release connections on ResultSetFuture#cancel. +- [bug] JAVA-393: Fix handling of SimpleStatement with values in query builder + batches. +- [bug] JAVA-417: Ensure pool is properly closed in onDown. +- [bug] JAVA-415: Fix tokenMap initialization at startup. +- [bug] JAVA-418: Avoid deadlock on close. + + +### 2.1.0-rc1 + +Merged from 2.0 branch: + +- [bug] JAVA-394: Ensure defunct connections are completely closed. +- [bug] JAVA-342, JAVA-390: Fix memory and resource leak on closed Sessions. + + +### 2.1.0-beta1 + +- [new] Support for User Defined Types and tuples +- [new] Simple object mapper + +Merged from 2.0 branch: everything up to 2.0.3 (included), and the following. + +- [improvement] JAVA-204: Better handling of dead connections. +- [bug] JAVA-373: Fix potential NPE in ControlConnection. +- [bug] JAVA-291: Throws NPE when passed null for a contact point. +- [bug] JAVA-315: Avoid LoadBalancingPolicy onDown+onUp at startup. +- [bug] JAVA-343: Avoid classloader leak in Tomcat. +- [bug] JAVA-387: Avoid deadlock in onAdd/onUp. +- [bug] JAVA-377, JAVA-391: Make metadata parsing more lenient. + + +### 2.0.12.2 + +- [bug] JAVA-1179: Request objects should be copied when executed. +- [improvement] JAVA-1182: Throw error when synchronous call made on I/O thread. +- [bug] JAVA-1184: Unwrap StatementWrappers when extracting column definitions. + + +### 2.0.12.1 + +- [bug] JAVA-994: Don't call on(Up|Down|Add|Remove) methods if Cluster is closed/closing. +- [improvement] JAVA-805: Document that metrics are null until Cluster is initialized. +- [bug] JAVA-1072: Ensure defunct connections are properly evicted from the pool. + + +### 2.0.12 + +- [bug] JAVA-950: Fix Cluster.connect with a case-sensitive keyspace. +- [improvement] JAVA-920: Downgrade "error creating pool" message to WARN. +- [bug] JAVA-954: Don't trigger reconnection before initialization complete. +- [improvement] JAVA-914: Avoid rejected tasks at shutdown. +- [improvement] JAVA-921: Add SimpleStatement.getValuesCount(). +- [bug] JAVA-901: Move call to connection.release() out of cancelHandler. +- [bug] JAVA-960: Avoid race in control connection shutdown. +- [bug] JAVA-656: Fix NPE in ControlConnection.updateLocationInfo. +- [bug] JAVA-966: Count uninitialized connections in conviction policy. +- [improvement] JAVA-917: Document SSL configuration. +- [improvement] JAVA-652: Add DCAwareRoundRobinPolicy builder. +- [improvement] JAVA-808: Add generic filtering policy that can be used to exclude specific DCs. + + +### 2.0.11 + +- [improvement] JAVA-718: Log streamid at the trace level on sending request and receiving response. +- [bug] JAVA-796: Fix SpeculativeExecutionPolicy.init() and close() are never called. +- [improvement] JAVA-710: Suppress unnecessary warning at shutdown. +- [improvement] #340: Allow DNS name with multiple A-records as contact point. +- [bug] JAVA-794: Allow tracing across multiple result pages. +- [bug] JAVA-737: DowngradingConsistencyRetryPolicy ignores write timeouts. +- [bug] JAVA-736: Forbid bind marker in QueryBuilder add/append/prepend. +- [bug] JAVA-712: Prevent QueryBuilder.quote() from applying duplicate double quotes. +- [bug] JAVA-688: Prevent QueryBuilder from trying to serialize raw string. +- [bug] JAVA-679: Support bind marker in QueryBuilder DELETE's list index. +- [improvement] JAVA-475: Improve QueryBuilder API for SELECT DISTINCT. +- [improvement] JAVA-225: Create values() function for Insert builder using List. +- [improvement] JAVA-702: Warn when ReplicationStrategy encounters invalid + replication factors. +- [improvement] JAVA-662: Add PoolingOptions method to set both core and max + connections. +- [improvement] JAVA-766: Do not include epoll JAR in binary distribution. +- [improvement] JAVA-726: Optimize internal copies of Request objects. +- [bug] JAVA-815: Preserve tracing across retries. +- [improvement] JAVA-709: New RetryDecision.tryNextHost(). +- [bug] JAVA-733: Handle function calls and raw strings as non-idempotent in QueryBuilder. +- [improvement] JAVA-765: Provide API to retrieve values of a Parameterized SimpleStatement. +- [improvement] JAVA-827: implement UPDATE .. IF EXISTS in QueryBuilder. +- [improvement] JAVA-618: Randomize contact points list to prevent hotspots. +- [improvement] JAVA-720: Surface the coordinator used on query failure. +- [bug] JAVA-792: Handle contact points removed during init. +- [improvement] JAVA-719: Allow PlainTextAuthProvider to change its credentials at runtime. +- [new feature] JAVA-151: Make it possible to register for SchemaChange Events. +- [improvement] JAVA-861: Downgrade "Asked to rebuild table" log from ERROR to INFO level. +- [improvement] JAVA-797: Provide an option to prepare statements only on one node. +- [improvement] JAVA-658: Provide an option to not re-prepare all statements in onUp. +- [improvement] JAVA-853: Customizable creation of netty timer. +- [bug] JAVA-859: Avoid quadratic ring processing with invalid replication factors. +- [improvement] JAVA-657: Debounce control connection queries. +- [bug] JAVA-784: LoadBalancingPolicy.distance() called before init(). +- [new feature] JAVA-828: Make driver-side metadata optional. +- [improvement] JAVA-544: Allow hosts to remain partially up. +- [improvement] JAVA-821, JAVA-822: Remove internal blocking calls and expose async session + creation. +- [improvement] JAVA-725: Use parallel calls when re-preparing statement on other + hosts. +- [bug] JAVA-629: Don't use connection timeout for unrelated internal queries. +- [bug] JAVA-892: Fix NPE in speculative executions when metrics disabled. + +Merged from 2.0.10_fixes branch: + +- [improvement] JAVA-756: Use Netty's pooled ByteBufAllocator by default. +- [improvement] JAVA-759: Expose "unsafe" paging state API. +- [bug] JAVA-767: Fix getObject by name. +- [bug] JAVA-768: Prevent race during pool initialization. + + +### 2.0.10.1 + +- [improvement] JAVA-756: Use Netty's pooled ByteBufAllocator by default. +- [improvement] JAVA-759: Expose "unsafe" paging state API. +- [bug] JAVA-767: Fix getObject by name. +- [bug] JAVA-768: Prevent race during pool initialization. + + +### 2.0.10 + +- [new feature] JAVA-518: Add AddressTranslater for EC2 multi-region deployment. +- [improvement] JAVA-533: Add connection heartbeat. +- [improvement] JAVA-568: Reduce level of logs on missing rpc_address. +- [improvement] JAVA-312, JAVA-681: Expose node token and range information. +- [bug] JAVA-595: Fix cluster name mismatch check at startup. +- [bug] JAVA-620: Fix guava dependency when using OSGI. +- [bug] JAVA-678: Fix handling of DROP events when ks name is case-sensitive. +- [improvement] JAVA-631: Use List instead of List in QueryBuilder API. +- [improvement] JAVA-654: Exclude Netty POM from META-INF in shaded JAR. +- [bug] JAVA-655: Quote single quotes contained in table comments in asCQLQuery method. +- [bug] JAVA-684: Empty TokenRange returned in a one token cluster. +- [improvement] JAVA-687: Expose TokenRange#contains. +- [new feature] JAVA-547: Expose values of BoundStatement. +- [new feature] JAVA-584: Add getObject to BoundStatement and Row. +- [improvement] JAVA-419: Improve connection pool resizing algorithm. +- [bug] JAVA-599: Fix race condition between pool expansion and shutdown. +- [improvement] JAVA-622: Upgrade Netty to 4.0.27. +- [improvement] JAVA-562: Coalesce frames before flushing them to the connection. +- [improvement] JAVA-583: Rename threads to indicate that they are for the driver. +- [new feature] JAVA-550: Expose paging state. +- [new feature] JAVA-646: Slow Query Logger. +- [improvement] JAVA-698: Exclude some errors from measurements in LatencyAwarePolicy. +- [bug] JAVA-641: Fix issue when executing a PreparedStatement from another cluster. +- [improvement] JAVA-534: Log keyspace xxx does not exist at WARN level. +- [improvement] JAVA-619: Allow Cluster subclasses to delegate to another instance. +- [new feature] JAVA-669: Expose an API to check for schema agreement after a + schema-altering statement. +- [improvement] JAVA-692: Make connection and pool creation fully async. +- [improvement] JAVA-505: Optimize connection use after reconnection. +- [improvement] JAVA-617: Remove "suspected" mechanism. +- [improvement] reverts JAVA-425: Don't mark connection defunct on client timeout. +- [new feature] JAVA-561: Speculative query executions. +- [bug] JAVA-666: Release connection before completing the ResultSetFuture. +- [new feature BETA] JAVA-723: Percentile-based variant of query logger and speculative + executions. +- [bug] JAVA-734: Fix buffer leaks when compression is enabled. + +Merged from 2.0.9_fixes branch: + +- [bug] JAVA-614: Prevent race between cancellation and query completion. +- [bug] JAVA-632: Prevent cancel and timeout from cancelling unrelated ResponseHandler if + streamId was already released and reused. +- [bug] JAVA-642: Fix issue when newly opened pool fails before we could mark the node UP. +- [bug] JAVA-613: Fix unwanted LBP notifications when a contact host is down. +- [bug] JAVA-651: Fix edge cases where a connection was released twice. +- [bug] JAVA-653: Fix edge cases in query cancellation. + + +### 2.0.9.2 + +- [bug] JAVA-651: Fix edge cases where a connection was released twice. +- [bug] JAVA-653: Fix edge cases in query cancellation. + + +### 2.0.9.1 + +- [bug] JAVA-614: Prevent race between cancellation and query completion. +- [bug] JAVA-632: Prevent cancel and timeout from cancelling unrelated ResponseHandler if + streamId was already released and reused. +- [bug] JAVA-642: Fix issue when newly opened pool fails before we could mark the node UP. +- [bug] JAVA-613: Fix unwanted LBP notifications when a contact host is down. + + +### 2.0.9 + +- [improvement] JAVA-538: Shade Netty dependency. +- [improvement] JAVA-543: Target schema refreshes more precisely. +- [bug] JAVA-546: Don't check rpc_address for control host. +- [improvement] JAVA-409: Improve message of NoHostAvailableException. +- [bug] JAVA-556: Rework connection reaper to avoid deadlock. +- [bug] JAVA-557: Avoid deadlock when multiple connections to the same host get write + errors. +- [improvement] JAVA-504: Make shuffle=true the default for TokenAwarePolicy. +- [bug] JAVA-577: Fix bug when SUSPECT reconnection succeeds, but one of the pooled + connections fails while bringing the node back up. +- [bug] JAVA-419: JAVA-587: Prevent faulty control connection from ignoring reconnecting hosts. +- temporarily revert "Add idle timeout to the connection pool". +- [bug] JAVA-593: Ensure updateCreatedPools does not add pools for suspected hosts. +- [bug] JAVA-594: Ensure state change notifications for a given host are handled serially. +- [bug] JAVA-597: Ensure control connection reconnects when control host is removed. + + +### 2.0.8 + +- [bug] JAVA-526: Fix token awareness for case-sensitive keyspaces and tables. +- [bug] JAVA-515: Check maximum number of values passed to SimpleStatement. +- [improvement] JAVA-532: Expose the driver version through the API. +- [improvement] JAVA-522: Optimize session initialization when some hosts are not + responsive. + + +### 2.0.7 + +- [bug] JAVA-449: Handle null pool in PooledConnection.release. +- [improvement] JAVA-425: Defunct connection on request timeout. +- [improvement] JAVA-426: Try next host when we get a SERVER_ERROR. +- [bug] JAVA-449, JAVA-460, JAVA-471: Handle race between query timeout and completion. +- [bug] JAVA-496: Fix DCAwareRoundRobinPolicy datacenter auto-discovery. +- [bug] JAVA-497: Ensure control connection does not trigger concurrent reconnects. +- [improvement] JAVA-472: Keep trying to reconnect on authentication errors. +- [improvement] JAVA-463: Expose close method on load balancing policy. +- [improvement] JAVA-459: Allow load balancing policy to trigger refresh for a single host. +- [bug] JAVA-493: Expose an API to cancel reconnection attempts. +- [bug] JAVA-503: Fix NPE when a connection fails during pool construction. +- [improvement] JAVA-423: Log datacenter name in DCAware policy's init when it is explicitly provided. +- [improvement] JAVA-504: Shuffle the replicas in TokenAwarePolicy.newQueryPlan. +- [improvement] JAVA-507: Make schema agreement wait tuneable. +- [improvement] JAVA-494: Document how to inject the driver metrics into another registry. +- [improvement] JAVA-419: Add idle timeout to the connection pool. +- [bug] JAVA-516: LatencyAwarePolicy does not shutdown executor on invocation of close. +- [improvement] JAVA-451: Throw an exception when DCAwareRoundRobinPolicy is built with + an explicit but null or empty local datacenter. +- [bug] JAVA-511: Fix check for local contact points in DCAware policy's init. +- [improvement] JAVA-457: Make timeout on saturated pool customizable. +- [improvement] JAVA-521: Downgrade Guava to 14.0.1. + + +### 2.0.6 + +- [bug] JAVA-397: Check cluster name when connecting to a new node. +- [bug] JAVA-326: Add missing CAS delete support in QueryBuilder. +- [bug] JAVA-363: Add collection and data length checks during serialization. +- [improvement] JAVA-329: Surface number of retries in metrics. +- [bug] JAVA-428: Do not use a host when no rpc_address found for it. +- [improvement] JAVA-358: Add ResultSet.wasApplied() for conditional queries. +- [bug] JAVA-349: Fix negative HostConnectionPool open count. +- [improvement] JAVA-436: Log more connection details at trace and debug levels. +- [bug] JAVA-445: Fix cluster shutdown. +- [improvement] JAVA-439: Expose child policy in chainable load balancing policies. + + +### 2.0.5 + +- [bug] JAVA-407: Release connections on ResultSetFuture#cancel. +- [bug] JAVA-393: Fix handling of SimpleStatement with values in query builder + batches. +- [bug] JAVA-417: Ensure pool is properly closed in onDown. +- [bug] JAVA-415: Fix tokenMap initialization at startup. +- [bug] JAVA-418: Avoid deadlock on close. + + +### 2.0.4 + +- [improvement] JAVA-204: Better handling of dead connections. +- [bug] JAVA-373: Fix potential NPE in ControlConnection. +- [bug] JAVA-291: Throws NPE when passed null for a contact point. +- [bug] JAVA-315: Avoid LoadBalancingPolicy onDown+onUp at startup. +- [bug] JAVA-343: Avoid classloader leak in Tomcat. +- [bug] JAVA-387: Avoid deadlock in onAdd/onUp. +- [bug] JAVA-377, JAVA-391: Make metadata parsing more lenient. +- [bug] JAVA-394: Ensure defunct connections are completely closed. +- [bug] JAVA-342, JAVA-390: Fix memory and resource leak on closed Sessions. + + +### 2.0.3 + +- [new] The new AbsractSession makes mocking of Session easier. +- [new] JAVA-309: Allow to trigger a refresh of connected hosts. +- [new] JAVA-265: New Session#getState method allows to grab information on + which nodes a session is connected to. +- [new] JAVA-327: Add QueryBuilder syntax for tuples in where clauses (syntax + introduced in Cassandra 2.0.6). +- [improvement] JAVA-359: Properly validate arguments of PoolingOptions methods. +- [bug] JAVA-368: Fix bogus rejection of BigInteger in 'execute with values'. +- [bug] JAVA-367: Signal connection failure sooner to avoid missing them. +- [bug] JAVA-337: Throw UnsupportedOperationException for protocol batch + setSerialCL. + +Merged from 1.0 branch: + +- [bug] JAVA-325: Fix periodic reconnection to down hosts. + + +### 2.0.2 + +- [api] The type of the map key returned by NoHostAvailable#getErrors has changed from + InetAddress to InetSocketAddress. Same for Initializer#getContactPoints return and + for AuthProvider#newAuthenticator. +- [api] JAVA-296: The default load balacing policy is now DCAwareRoundRobinPolicy, and the local + datacenter is automatically picked based on the first connected node. Furthermore, + the TokenAwarePolicy is also used by default. +- [new] JAVA-145: New optional AddressTranslater. +- [bug] JAVA-321: Don't remove quotes on keyspace in the query builder. +- [bug] JAVA-320: Fix potential NPE while cluster undergo schema changes. +- [bug] JAVA-319: Fix thread-safety of page fetching. +- [bug] JAVA-318: Fix potential NPE using fetchMoreResults. + +Merged from 1.0 branch: + +- [new] JAVA-179: Expose the name of the partitioner in use in the cluster metadata. +- [new] Add new WhiteListPolicy to limit the nodes connected to a particular list. +- [improvement] JAVA-289: Do not hop DC for LOCAL_* CL in DCAwareRoundRobinPolicy. +- [bug] JAVA-313: Revert back to longs for dates in the query builder. +- [bug] JAVA-314: Don't reconnect to nodes ignored by the load balancing policy. + + +### 2.0.1 + +- [improvement] JAVA-278: Handle the static columns introduced in Cassandra 2.0.6. +- [improvement] JAVA-208: Add Cluster#newSession method to create Session without connecting + right away. +- [bug] JAVA-279: Add missing iso8601 patterns for parsing dates. +- [bug] Properly parse BytesType as the blob type. +- [bug] JAVA-280: Potential NPE when parsing schema of pre-CQL tables of C* 1.2 nodes. + +Merged from 1.0 branch: + +- [bug] JAVA-275: LatencyAwarePolicy.Builder#withScale doesn't set the scale. +- [new] JAVA-114: Add methods to check if a Cluster/Session instance has been closed already. + + +### 2.0.0 + +- [api] JAVA-269: Case sensitive identifier by default in Metadata. +- [bug] JAVA-274: Fix potential NPE in Cluster#connect. + +Merged from 1.0 branch: + +- [bug] JAVA-263: Always return the PreparedStatement object that is cache internally. +- [bug] JAVA-261: Fix race when multiple connect are done in parallel. +- [bug] JAVA-270: Don't connect at all to nodes that are ignored by the load balancing + policy. + + +### 2.0.0-rc3 + +- [improvement] The protocol version 1 is now supported (features only supported by the + version 2 of the protocol throw UnsupportedFeatureException). +- [improvement] JAVA-195: Make most main objects interface to facilitate testing/mocking. +- [improvement] Adds new getStatements and clear methods to BatchStatement. +- [api] JAVA-247: Renamed shutdown to closeAsync and ShutdownFuture to CloseFuture. Clustering + and Session also now implement Closeable. +- [bug] JAVA-232: Fix potential thread leaks when shutting down Metrics. +- [bug] JAVA-231: Fix potential NPE in HostConnectionPool. +- [bug] JAVA-244: Avoid NPE when node is in an unconfigured DC. +- [bug] JAVA-258: Don't block for scheduled reconnections on Cluster#close. + +Merged from 1.0 branch: + +- [new] JAVA-224: Added Session#prepareAsync calls. +- [new] JAVA-249: Added Cluster#getLoggedKeyspace. +- [improvement] Avoid preparing a statement multiple time per host with multiple sessions. +- [bug] JAVA-255: Make sure connections are returned to the right pools. +- [bug] JAVA-264: Use date string in query build to work-around CASSANDRA-6718. + + +### 2.0.0-rc2 + +- [new] JAVA-207: Add LOCAL_ONE consistency level support (requires using C* 2.0.2+). +- [bug] JAVA-219: Fix parsing of counter types. +- [bug] JAVA-218: Fix missing whitespace for IN clause in the query builder. +- [bug] JAVA-221: Fix replicas computation for token aware balancing. + +Merged from 1.0 branch: + +- [bug] JAVA-213: Fix regression from JAVA-201. +- [improvement] New getter to obtain a snapshot of the scores maintained by + LatencyAwarePolicy. + + +### 2.0.0-rc1 + +- [new] JAVA-199: Mark compression dependencies optional in maven. +- [api] Renamed TableMetadata#getClusteringKey to TableMetadata#getClusteringColumns. + +Merged from 1.0 branch: + +- [new] JAVA-142: OSGi bundle. +- [improvement] JAVA-205: Make collections returned by Row immutable. +- [improvement] JAVA-203: Limit internal thread pool size. +- [bug] JAVA-201: Don't retain unused PreparedStatement in memory. +- [bug] Add missing clustering order info in TableMetadata +- [bug] JAVA-196: Allow bind markers for collections in the query builder. + + +### 2.0.0-beta2 + +- [api] BoundStatement#setX(String, X) methods now set all values (if there is + more than one) having the provided name, not just the first occurence. +- [api] The Authenticator interface now has a onAuthenticationSuccess method that + allows to handle the potential last token sent by the server. +- [new] The query builder don't serialize large values to strings anymore by + default by making use the new ability to send values alongside the query string. +- [new] JAVA-140: The query builder has been updated for new CQL features. +- [bug] Fix exception when a conditional write timeout C* side. +- [bug] JAVA-182: Ensure connection is created when Cluster metadata are asked for. +- [bug] JAVA-187: Fix potential NPE during authentication. + + +### 2.0.0-beta1 + +- [api] The 2.0 version is an API-breaking upgrade of the driver. While most + of the breaking changes are minor, there are too numerous to be listed here + and you are encouraged to look at the Upgrade_guide_to_2.0 file that describe + those changes in details. +- [new] LZ4 compression is supported for the protocol. +- [new] JAVA-39: The driver does not depend on cassandra-all anymore. +- [new] New BatchStatement class allows to execute batch other statements. +- [new] Large ResultSet are now paged (incrementally fetched) by default. +- [new] SimpleStatement support values for bind-variables, to allow + prepare+execute behavior with one roundtrip. +- [new] Query parameters defaults (Consistency level, page size, ...) can be + configured globally. +- [new] New Cassandra 2.0 SERIAL and LOCAL_SERIAL consistency levels are + supported. +- [new] JAVA-116: Cluster#shutdown now waits for ongoing queries to complete by default. +- [new] Generic authentication through SASL is now exposed. +- [bug] JAVA-88: TokenAwarePolicy now takes all replica into account, instead of only the + first one. + + +### 1.0.5 + +- [new] JAVA-142: OSGi bundle. +- [new] JAVA-207: Add support for ConsistencyLevel.LOCAL_ONE; note that this + require Cassandra 1.2.12+. +- [improvement] JAVA-205: Make collections returned by Row immutable. +- [improvement] JAVA-203: Limit internal thread pool size. +- [improvement] New getter to obtain a snapshot of the scores maintained by + LatencyAwarePolicy. +- [improvement] JAVA-222: Avoid synchronization when getting codec for collection + types. +- [bug] JAVA-201, JAVA-213: Don't retain unused PreparedStatement in memory. +- [bug] Add missing clustering order info in TableMetadata +- [bug] JAVA-196: Allow bind markers for collections in the query builder. + + +### 1.0.4 + +- [api] JAVA-163: The Cluster.Builder#poolingOptions and Cluster.Builder#socketOptions + are now deprecated. They are replaced by the new withPoolingOptions and + withSocketOptions methods. +- [new] JAVA-129: A new LatencyAwarePolicy wrapping policy has been added, allowing to + add latency awareness to a wrapped load balancing policy. +- [new] JAVA-161: Cluster.Builder#deferInitialization: Allow defering cluster initialization. +- [new] JAVA-117: Add truncate statement in query builder. +- [new] JAVA-106: Support empty IN in the query builder. +- [bug] JAVA-166: Fix spurious "No current pool set; this should not happen" error + message. +- [bug] JAVA-184: Fix potential overflow in RoundRobinPolicy and correctly errors if + a balancing policy throws. +- [bug] Don't release Stream ID for timeouted queries (unless we do get back + the response) +- [bug] Correctly escape identifiers and use fully qualified table names when + exporting schema as string. + + +### 1.0.3 + +- [api] The query builder now correctly throw an exception when given a value + of a type it doesn't know about. +- [new] SocketOptions#setReadTimeout allows to set a timeout on how long we + wait for the answer of one node. See the javadoc for more details. +- [new] New Session#prepare method that takes a Statement. +- [bug] JAVA-143: Always take per-query CL, tracing, etc. into account for QueryBuilder + statements. +- [bug] Temporary fixup for TimestampType when talking to C* 2.0 nodes. + + +### 1.0.2 + +- [api] Host#getMonitor and all Host.HealthMonitor methods have been + deprecated. The new Host#isUp method is now prefered to the method + in the monitor and you should now register Host.StateListener against + the Cluster object directly (registering against a host HealthMonitor + was much more limited anyway). +- [new] JAVA-92: New serialize/deserialize methods in DataType to serialize/deserialize + values to/from bytes. +- [new] JAVA-128: New getIndexOf() method in ColumnDefinitions to find the index of + a given column name. +- [bug] JAVA-131: Fix a bug when thread could get blocked while setting the current + keyspace. +- [bug] JAVA-136: Quote inet addresses in the query builder since CQL3 requires it. + + +### 1.0.1 + +- [api] JAVA-100: Function call handling in the query builder has been modified in a + backward incompatible way. Function calls are not parsed from string values + anymore as this wasn't safe. Instead the new 'fcall' method should be used. +- [api] Some typos in method names in PoolingOptions have been fixed in a + backward incompatible way before the API get widespread. +- [bug] JAVA-123: Don't destroy composite partition key with BoundStatement and + TokenAwarePolicy. +- [new] null values support in the query builder. +- [new] JAVA-5: SSL support (requires C* >= 1.2.1). +- [new] JAVA-113: Allow generating unlogged batch in the query builder. +- [improvement] Better error message when no host are available. +- [improvement] Improves performance of the stress example application been. + + +### 1.0.0 + +- [api] The AuthInfoProvider has be (temporarily) removed. Instead, the + Cluster builder has a new withCredentials() method to provide a username + and password for use with Cassandra's PasswordAuthenticator. Custom + authenticator will be re-introduced in a future version but are not + supported at the moment. +- [api] The isMetricsEnabled() method in Configuration has been replaced by + getMetricsOptions(). An option to disabled JMX reporting (on by default) + has been added. +- [bug] JAVA-91: Don't make default load balancing policy a static singleton since it + is stateful. + + +### 1.0.0-RC1 + +- [new] JAVA-79: Null values are now supported in BoundStatement (but you will need at + least Cassandra 1.2.3 for it to work). The API of BoundStatement has been + slightly changed so that not binding a variable is not an error anymore, + the variable is simply considered null by default. The isReady() method has + been removed. +- [improvement] JAVA-75: The Cluster/Session shutdown methods now properly block until + the shutdown is complete. A version with at timeout has been added. +- [bug] JAVA-44: Fix use of CQL3 functions in the query builder. +- [bug] JAVA-77: Fix case where multiple schema changes too quickly wouldn't work + (only triggered when 0.0.0.0 was used for the rpc_address on the Cassandra + nodes). +- [bug] JAVA-72: Fix IllegalStateException thrown due to a reconnection made on an I/O + thread. +- [bug] JAVA-82: Correctly reports errors during authentication phase. + + +### 1.0.0-beta2 + +- [new] JAVA-51, JAVA-60, JAVA-58: Support blob constants, BigInteger, BigDecimal and counter batches in + the query builder. +- [new] JAVA-61: Basic support for custom CQL3 types. +- [new] JAVA-65: Add "execution infos" for a result set (this also move the query + trace in the new ExecutionInfos object, so users of beta1 will have to + update). +- [bug] JAVA-62: Fix failover bug in DCAwareRoundRobinPolicy. +- [bug] JAVA-66: Fix use of bind markers for routing keys in the query builder. + + +### 1.0.0-beta1 + +- initial release diff --git a/ci/create-user.sh b/ci/create-user.sh new file mode 100644 index 00000000000..fb193df9a00 --- /dev/null +++ b/ci/create-user.sh @@ -0,0 +1,60 @@ +#!/bin/bash +# 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. + +################################ +# +# Prep +# +################################ + +if [ "$1" == "-h" ]; then + echo "$0 [-h] " + echo " this script is used internally by other scripts in the same directory to create a user with the running host user's same uid and gid" + exit 1 +fi + +# arguments +username=$1 +uid=$2 +gid=$3 +BUILD_HOME=$4 + +################################ +# +# Main +# +################################ + +# disable git directory ownership checks +su ${username} -c "git config --global safe.directory '*'" + +if grep "^ID=" /etc/os-release | grep -q 'debian\|ubuntu' ; then + deluser docker + adduser --quiet --disabled-login --no-create-home --uid $uid --gecos ${username} ${username} + groupmod --non-unique -g $gid $username + gpasswd -a ${username} sudo >/dev/null +else + adduser --no-create-home --uid $uid ${username} +fi + +# sudo priviledges +echo "${username} ALL=(root) NOPASSWD:ALL" > /etc/sudoers.d/${username} +chmod 0440 /etc/sudoers.d/${username} + +# proper permissions +chown -R ${username}:${username} /home/docker +chmod og+wx ${BUILD_HOME} \ No newline at end of file diff --git a/ci/run-tests.sh b/ci/run-tests.sh new file mode 100755 index 00000000000..5268bdd7113 --- /dev/null +++ b/ci/run-tests.sh @@ -0,0 +1,12 @@ +#!/bin/bash -x + +. ~/.jabba/jabba.sh +. ~/env.txt +cd $(dirname "$(readlink -f "$0")")/.. +printenv | sort +mvn -B -V install -DskipTests -Dmaven.javadoc.skip=true +jabba use ${TEST_JAVA_VERSION} +# Find out the latest patch version of Cassandra +PATCH_SERVER_VERSION=$(curl -s https://downloads.apache.org/cassandra/ | grep -oP '(?<=href=\")[0-9]+\.[0-9]+\.[0-9]+(?=)' | sort -rV | uniq -w 3 | grep $SERVER_VERSION) +printenv | sort +mvn -B -V verify -T 1 -Ptest-jdk-${TEST_JAVA_MAJOR_VERSION} -DtestJavaHome=$(jabba which ${TEST_JAVA_VERSION}) -Dccm.version=${PATCH_SERVER_VERSION} -Dccm.dse=false -Dmaven.test.failure.ignore=true -Dmaven.javadoc.skip=true diff --git a/core-shaded/pom.xml b/core-shaded/pom.xml index 56748f0f5d2..7d173e2783b 100644 --- a/core-shaded/pom.xml +++ b/core-shaded/pom.xml @@ -1,13 +1,15 @@ 4.0.0 - - com.datastax.oss + org.apache.cassandra java-driver-parent - 4.0.2-SNAPSHOT + 4.19.4-SNAPSHOT - java-driver-core-shaded - - DataStax Java driver for Apache Cassandra(R) - core with shaded deps - + Apache Cassandra Java Driver - core with shaded deps + + + + ${project.groupId} + java-driver-bom + ${project.version} + pom + import + + + - com.datastax.oss + org.apache.cassandra java-driver-core - ${project.version} - + + + src/main/resources + + + ${project.basedir}/.. + + LICENSE + NOTICE_binary.txt + NOTICE.txt + + META-INF + + maven-shade-plugin @@ -123,19 +160,50 @@ - com.datastax.oss:java-driver-core + org.apache.cassandra:java-driver-core io.netty:* + com.fasterxml.jackson.core:* + io.netty com.datastax.oss.driver.shaded.netty + + com.fasterxml.jackson + com.datastax.oss.driver.shaded.fasterxml.jackson + + + + + org.apache.cassandra:* + + + META-INF/MANIFEST.MF + META-INF/maven/** + + + + io.netty:* + + META-INF/** + + + + com.fasterxml.jackson.core:* + + META-INF/** + + + @@ -152,21 +220,12 @@ - com.datastax.oss + org.apache.cassandra java-driver-core-shaded - ${project.version} jar ${project.build.outputDirectory} - - - META-INF/maven/com.datastax.oss/java-driver-core/**, - META-INF/maven/io.netty/**, - @@ -179,9 +238,8 @@ - com.datastax.oss + org.apache.cassandra java-driver-core-shaded - ${project.version} jar sources ${project.build.directory}/shaded-sources @@ -191,34 +249,30 @@ + - maven-javadoc-plugin + com.google.code.maven-replacer-plugin + replacer + 1.5.3 - attach-shaded-javadocs + shade-graalvm-files + package - jar + replace - - ${project.build.directory}/shaded-sources - - com.datastax.oss.driver.internal:com.datastax.oss.driver.shaded - - - - - org.jctools - jctools-core - 2.1.2 - - - + + false + ${project.build.directory}/classes/META-INF/native-image/com.datastax.oss/java-driver-core/reflection.json,${project.build.directory}/shaded-sources/META-INF/native-image/com.datastax.oss/java-driver-core/reflection.json + + + io.netty + com.datastax.oss.driver.shaded.netty + + + org.apache.felix @@ -233,49 +287,44 @@ + com.datastax.oss.driver.core com.datastax.oss.driver.core * - - !com.datastax.oss.driver.shaded.netty.*, - !jnr.*, - !net.jcip.annotations.*, - !edu.umd.cs.findbugs.annotations.*, - !com.google.protobuf.*, - !com.jcraft.jzlib.*, - !com.ning.compress.*, - !lzma.sdk.*, - !net.jpountz.xxhash.*, - !org.bouncycastle.*, - !org.conscrypt.*, - !org.apache.commons.logging.*, - !org.apache.log4j.*, - !org.apache.logging.log4j.*, - !org.eclipse.jetty.*, - !org.jboss.marshalling.*, - !sun.misc.*, - !sun.security.*, - * + !com.datastax.oss.driver.shaded.netty.*, !com.datastax.oss.driver.shaded.fasterxml.jackson.*, + !net.jcip.annotations.*, !edu.umd.cs.findbugs.annotations.*, + !org.graalvm.*, !com.oracle.svm.*, + jnr.*;resolution:=optional, com.esri.core.geometry.*;resolution:=optional,org.reactivestreams.*;resolution:=optional, org.apache.tinkerpop.*;resolution:=optional, org.javatuples.*;resolution:=optional, reactor.blockhound.*;resolution:=optional, + !com.google.protobuf.*, !com.jcraft.jzlib.*, !com.ning.compress.*, !lzma.sdk.*, !net.jpountz.xxhash.*, !org.bouncycastle.*, !org.conscrypt.*, !org.apache.commons.logging.*, !org.apache.log4j.*, !org.apache.logging.log4j.*, !org.eclipse.jetty.*, !org.jboss.marshalling.*, !sun.misc.*, !sun.security.*, !com.barchart.udt.*, !com.fasterxml.aalto.*, !com.sun.nio.sctp.*, !gnu.io.*, !org.xml.sax.*, !org.w3c.dom.*, !com.aayushatharva.brotli4j.*, !com.github.luben.zstd.*, * - - com.datastax.oss.driver.api.core.*, - com.datastax.oss.driver.internal.core.*, - com.datastax.oss.driver.shaded.netty.*, - + com.datastax.oss.driver.api.core.*, com.datastax.oss.driver.internal.core.*, com.datastax.dse.driver.api.core.*, com.datastax.dse.driver.internal.core.*, com.datastax.oss.driver.shaded.netty.*, com.datastax.oss.driver.shaded.fasterxml.jackson.*, true diff --git a/core-shaded/src/assembly/shaded-jar.xml b/core-shaded/src/assembly/shaded-jar.xml index 3a735f36d2a..449eb77bd1a 100644 --- a/core-shaded/src/assembly/shaded-jar.xml +++ b/core-shaded/src/assembly/shaded-jar.xml @@ -1,12 +1,15 @@ + - + shaded-jar jar @@ -41,4 +42,4 @@ pom.xml - \ No newline at end of file + diff --git a/core/console.scala b/core/console.scala index 0ae13620ff8..491add7edea 100644 --- a/core/console.scala +++ b/core/console.scala @@ -1,3 +1,22 @@ +/* + * 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. + */ + /* * Allows quick manual tests from the Scala console: * @@ -36,4 +55,4 @@ println("********************************************") def fire(event: AnyRef)(implicit session: CqlSession): Unit = { session.getContext.asInstanceOf[InternalDriverContext].getEventBus().fire(event) -} \ No newline at end of file +} diff --git a/core/pom.xml b/core/pom.xml index edf5655be32..59306b175cd 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -1,12 +1,15 @@ + 4.0.0 - - com.datastax.oss + org.apache.cassandra java-driver-parent - 4.0.2-SNAPSHOT + 4.19.4-SNAPSHOT - java-driver-core bundle - - DataStax Java driver for Apache Cassandra(R) - core - + Apache Cassandra Java Driver - core + + + + ${project.groupId} + java-driver-bom + ${project.version} + pom + import + + + com.datastax.oss @@ -39,8 +49,8 @@ netty-handler - com.datastax.oss - java-driver-shaded-guava + org.apache.cassandra + java-driver-guava-shaded com.typesafe @@ -53,10 +63,6 @@ These dependencies are recommended but not mandatory, the driver will fall back to pure-Java implementations if they are not available at runtime. --> - - com.github.jnr - jnr-ffi - com.github.jnr jnr-posix @@ -67,7 +73,7 @@ true - org.lz4 + at.yawk.lz4 lz4-java true @@ -83,13 +89,57 @@ org.hdrhistogram HdrHistogram + + com.esri.geometry + esri-geometry-api + true + + + org.apache.tinkerpop + gremlin-core + true + + + org.apache.tinkerpop + tinkergraph-gremlin + true + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-databind + + + org.reactivestreams + reactive-streams + com.github.stephenc.jcip jcip-annotations + provided com.github.spotbugs spotbugs-annotations + provided + + + org.graalvm.sdk + graal-sdk + provided + + + org.graalvm.nativeimage + svm + provided + + + io.projectreactor.tools + blockhound + provided ch.qos.logback @@ -116,8 +166,32 @@ mockito-core test + + io.reactivex.rxjava2 + rxjava + test + + + org.reactivestreams + reactive-streams-tck + test + + + org.awaitility + awaitility + test + + + org.testng + testng + test + + + com.github.tomakehurst + wiremock + test + - @@ -134,10 +208,42 @@ false + + ${project.basedir}/.. + + LICENSE + NOTICE_binary.txt + NOTICE.txt + + META-INF + + + + src/test/resources + + project.properties + + true + + + src/test/resources + + project.properties + + false + + maven-jar-plugin + + + + com.datastax.oss.driver.core + + + test-jar @@ -155,13 +261,37 @@ maven-surefire-plugin + ${testing.jvm}/bin/java + ${mockitoopens.argline} + 1 listener com.datastax.oss.driver.DriverRunListener + + + junit + false + + + suitename + Reactive Streams TCK + + + + org.apache.maven.surefire + surefire-junit47 + ${surefire.version} + + + org.apache.maven.surefire + surefire-testng + ${surefire.version} + + org.apache.felix @@ -180,24 +310,47 @@ (so reflection-based loading of policies works) --> * - - !net.jcip.annotations.*, - !edu.umd.cs.findbugs.annotations.*, - !jnr.*, - * + !net.jcip.annotations.*, !edu.umd.cs.findbugs.annotations.*, + !org.graalvm.*, !com.oracle.svm.*, + jnr.*;resolution:=optional, com.esri.core.geometry.*;resolution:=optional, org.reactivestreams.*;resolution:=optional, org.apache.tinkerpop.*;resolution:=optional, org.javatuples.*;resolution:=optional, reactor.blockhound.*;resolution:=optional, * - - com.datastax.oss.driver.*.core.* - + com.datastax.oss.driver.*.core.*, com.datastax.dse.driver.*.core.* + + maven-dependency-plugin + + + generate-dependency-list + + list + + generate-resources + + runtime + true + com.datastax.cassandra,com.datastax.dse + ${project.build.outputDirectory}/com/datastax/dse/driver/internal/deps.txt + + + + diff --git a/core/revapi.json b/core/revapi.json index a82e54e2a73..d56566bc2b9 100644 --- a/core/revapi.json +++ b/core/revapi.json @@ -1,5 +1,3 @@ -// Configures Revapi (https://revapi.org/getting-started.html) to check API compatibility between -// successive driver versions. { "revapi": { "java": { @@ -7,8 +5,8 @@ "packages": { "regex": true, "exclude": [ - "com\\.datastax\\.oss\\.protocol\\.internal(\\..+)?", - "com\\.datastax\\.oss\\.driver\\.internal(\\..+)?", + "com\\.datastax\\.(oss|dse)\\.protocol\\.internal(\\..+)?", + "com\\.datastax\\.(oss|dse)\\.driver\\.internal(\\..+)?", "com\\.datastax\\.oss\\.driver\\.shaded(\\..+)?", "org\\.assertj(\\..+)?" ] @@ -17,4731 +15,1553 @@ }, "ignore": [ { - "code": "java.method.removed", - "old": "method com.datastax.oss.driver.api.core.cql.BatchStatementBuilder com.datastax.oss.driver.api.core.cql.BatchStatementBuilder::withKeyspace(com.datastax.oss.driver.api.core.CqlIdentifier)", - "justification": "JAVA-2164: Rename statement builder methods to setXxx" - }, - { - "code": "java.method.removed", - "old": "method com.datastax.oss.driver.api.core.cql.BatchStatementBuilder com.datastax.oss.driver.api.core.cql.BatchStatementBuilder::withKeyspace(java.lang.String)", - "justification": "JAVA-2164: Rename statement builder methods to setXxx" - }, - { - "code": "java.method.removed", - "old": "method com.datastax.oss.driver.api.core.cql.SimpleStatementBuilder com.datastax.oss.driver.api.core.cql.SimpleStatementBuilder::withKeyspace(com.datastax.oss.driver.api.core.CqlIdentifier)", - "justification": "JAVA-2164: Rename statement builder methods to setXxx" - }, - { - "code": "java.method.removed", - "old": "method com.datastax.oss.driver.api.core.cql.SimpleStatementBuilder com.datastax.oss.driver.api.core.cql.SimpleStatementBuilder::withKeyspace(java.lang.String)", - "justification": "JAVA-2164: Rename statement builder methods to setXxx" - }, - { - "code": "java.method.removed", - "old": "method com.datastax.oss.driver.api.core.cql.SimpleStatementBuilder com.datastax.oss.driver.api.core.cql.SimpleStatementBuilder::withQuery(java.lang.String)", - "justification": "JAVA-2164: Rename statement builder methods to setXxx" - }, - { - "code": "java.method.removed", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.StatementBuilder>, StatementT>, StatementT extends com.datastax.oss.driver.api.core.cql.Statement>>::withConsistencyLevel(com.datastax.oss.driver.api.core.ConsistencyLevel)", - "justification": "JAVA-2164: Rename statement builder methods to setXxx" + "code": "java.class.nonPublicPartOfAPI", + "old": "class com.datastax.oss.driver.internal.core.config.typesafe.TypesafeDriverExecutionProfile.Base", + "justification": "CASSJAVA-102: Fix spurious complaints about optional dependencies" }, { - "code": "java.method.removed", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.StatementBuilder>, StatementT>, StatementT extends com.datastax.oss.driver.api.core.cql.Statement>>::withExecutionProfile(com.datastax.oss.driver.api.core.config.DriverExecutionProfile)", - "justification": "JAVA-2164: Rename statement builder methods to setXxx" - }, - { - "code": "java.method.removed", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.StatementBuilder>, StatementT>, StatementT extends com.datastax.oss.driver.api.core.cql.Statement>>::withExecutionProfileName(java.lang.String)", - "justification": "JAVA-2164: Rename statement builder methods to setXxx" + "code": "java.class.nonPublicPartOfAPI", + "old": "class com.fasterxml.jackson.databind.type.TypeParser.MyTokenizer", + "justification": "CASSJAVA-102: Fix spurious complaints about optional dependencies" }, { - "code": "java.method.removed", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.StatementBuilder>, StatementT>, StatementT extends com.datastax.oss.driver.api.core.cql.Statement>>::withIdempotence(java.lang.Boolean)", - "justification": "JAVA-2164: Rename statement builder methods to setXxx" - }, - { - "code": "java.method.removed", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.StatementBuilder>, StatementT>, StatementT extends com.datastax.oss.driver.api.core.cql.Statement>>::withNode(com.datastax.oss.driver.api.core.metadata.Node)", - "justification": "JAVA-2164: Rename statement builder methods to setXxx" + "code": "java.class.nonPublicPartOfAPI", + "old": "class org.apache.tinkerpop.shaded.jackson.databind.type.TypeParser.MyTokenizer", + "justification": "CASSJAVA-102: Fix spurious complaints about optional dependencies" }, { - "code": "java.method.removed", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.StatementBuilder>, StatementT>, StatementT extends com.datastax.oss.driver.api.core.cql.Statement>>::withPageSize(int)", - "justification": "JAVA-2164: Rename statement builder methods to setXxx" + "code": "java.class.externalClassExposedInAPI", + "justification": "CASSJAVA-102: Migrate revapi config into dedicated config files, ported from pom.xml" }, { - "code": "java.method.removed", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.StatementBuilder>, StatementT>, StatementT extends com.datastax.oss.driver.api.core.cql.Statement>>::withPagingState(java.nio.ByteBuffer)", - "justification": "JAVA-2164: Rename statement builder methods to setXxx" + "code": "java.method.varargOverloadsOnlyDifferInVarargParameter", + "justification": "CASSJAVA-102: Migrate revapi config into dedicated config files, ported from pom.xml" }, { - "code": "java.method.removed", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.StatementBuilder>, StatementT>, StatementT extends com.datastax.oss.driver.api.core.cql.Statement>>::withRoutingKey(java.nio.ByteBuffer)", - "justification": "JAVA-2164: Rename statement builder methods to setXxx" + "ignore": true, + "code": "java.field.serialVersionUIDUnchanged", + "old": "field com.fasterxml.jackson.annotation.JacksonInject.Value.serialVersionUID", + "new": "field com.fasterxml.jackson.annotation.JacksonInject.Value.serialVersionUID", + "serialVersionUID": "1", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.method.removed", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.StatementBuilder>, StatementT>, StatementT extends com.datastax.oss.driver.api.core.cql.Statement>>::withRoutingKeyspace(com.datastax.oss.driver.api.core.CqlIdentifier)", - "justification": "JAVA-2164: Rename statement builder methods to setXxx" + "ignore": true, + "code": "java.method.numberOfParametersChanged", + "old": "method void com.fasterxml.jackson.annotation.JacksonInject.Value::(java.lang.Object, java.lang.Boolean)", + "new": "method void com.fasterxml.jackson.annotation.JacksonInject.Value::(java.lang.Object, java.lang.Boolean, java.lang.Boolean)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.method.removed", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.StatementBuilder>, StatementT>, StatementT extends com.datastax.oss.driver.api.core.cql.Statement>>::withRoutingKeyspace(java.lang.String)", - "justification": "JAVA-2164: Rename statement builder methods to setXxx" + "ignore": true, + "code": "java.field.serialVersionUIDUnchanged", + "old": "field com.fasterxml.jackson.annotation.JsonAutoDetect.Value.serialVersionUID", + "new": "field com.fasterxml.jackson.annotation.JsonAutoDetect.Value.serialVersionUID", + "serialVersionUID": "1", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.method.removed", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.StatementBuilder>, StatementT>, StatementT extends com.datastax.oss.driver.api.core.cql.Statement>>::withRoutingToken(com.datastax.oss.driver.api.core.metadata.token.Token)", - "justification": "JAVA-2164: Rename statement builder methods to setXxx" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.annotation.JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_VALUES", + "new": "field com.fasterxml.jackson.annotation.JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_VALUES", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.method.removed", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.StatementBuilder>, StatementT>, StatementT extends com.datastax.oss.driver.api.core.cql.Statement>>::withSerialConsistencyLevel(com.datastax.oss.driver.api.core.ConsistencyLevel)", - "justification": "JAVA-2164: Rename statement builder methods to setXxx" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.annotation.JsonFormat.Feature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE", + "new": "field com.fasterxml.jackson.annotation.JsonFormat.Feature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.method.removed", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.StatementBuilder>, StatementT>, StatementT extends com.datastax.oss.driver.api.core.cql.Statement>>::withTimeout(java.time.Duration)", - "justification": "JAVA-2164: Rename statement builder methods to setXxx" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.annotation.JsonFormat.Feature.WRITE_DATES_WITH_ZONE_ID", + "new": "field com.fasterxml.jackson.annotation.JsonFormat.Feature.WRITE_DATES_WITH_ZONE_ID", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.method.removed", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.StatementBuilder>, StatementT>, StatementT extends com.datastax.oss.driver.api.core.cql.Statement>>::withTimestamp(long)", - "justification": "JAVA-2164: Rename statement builder methods to setXxx" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.annotation.JsonFormat.Feature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS", + "new": "field com.fasterxml.jackson.annotation.JsonFormat.Feature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.method.removed", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.StatementBuilder>, StatementT>, StatementT extends com.datastax.oss.driver.api.core.cql.Statement>>::withTracing()", - "justification": "JAVA-2164: Rename statement builder methods to setXxx" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.annotation.JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED", + "new": "field com.fasterxml.jackson.annotation.JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.method.parameterTypeChanged", - "old": "parameter void com.datastax.oss.driver.api.core.UnsupportedProtocolVersionException::(===java.net.SocketAddress===, java.lang.String, java.util.List)", - "new": "parameter void com.datastax.oss.driver.api.core.UnsupportedProtocolVersionException::(===com.datastax.oss.driver.api.core.metadata.EndPoint===, java.lang.String, java.util.List)", - "justification": "JAVA-2165: Abstract node connection information" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.annotation.JsonFormat.Feature.WRITE_SORTED_MAP_ENTRIES", + "new": "field com.fasterxml.jackson.annotation.JsonFormat.Feature.WRITE_SORTED_MAP_ENTRIES", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.method.parameterTypeChanged", - "old": "parameter com.datastax.oss.driver.api.core.UnsupportedProtocolVersionException com.datastax.oss.driver.api.core.UnsupportedProtocolVersionException::forNegotiation(===java.net.SocketAddress===, java.util.List)", - "new": "parameter com.datastax.oss.driver.api.core.UnsupportedProtocolVersionException com.datastax.oss.driver.api.core.UnsupportedProtocolVersionException::forNegotiation(===com.datastax.oss.driver.api.core.metadata.EndPoint===, java.util.List)", - "justification": "JAVA-2165: Abstract node connection information" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.annotation.JsonFormat.Shape.ANY", + "new": "field com.fasterxml.jackson.annotation.JsonFormat.Shape.ANY", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.method.parameterTypeChanged", - "old": "parameter com.datastax.oss.driver.api.core.UnsupportedProtocolVersionException com.datastax.oss.driver.api.core.UnsupportedProtocolVersionException::forSingleAttempt(===java.net.SocketAddress===, com.datastax.oss.driver.api.core.ProtocolVersion)", - "new": "parameter com.datastax.oss.driver.api.core.UnsupportedProtocolVersionException com.datastax.oss.driver.api.core.UnsupportedProtocolVersionException::forSingleAttempt(===com.datastax.oss.driver.api.core.metadata.EndPoint===, com.datastax.oss.driver.api.core.ProtocolVersion)", - "justification": "JAVA-2165: Abstract node connection information" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.annotation.JsonFormat.Shape.ARRAY", + "new": "field com.fasterxml.jackson.annotation.JsonFormat.Shape.ARRAY", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.method.removed", - "old": "method java.net.SocketAddress com.datastax.oss.driver.api.core.UnsupportedProtocolVersionException::getAddress()", - "justification": "JAVA-2165: Abstract node connection information" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.annotation.JsonFormat.Shape.BINARY", + "new": "field com.fasterxml.jackson.annotation.JsonFormat.Shape.BINARY", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.method.parameterTypeChanged", - "old": "parameter com.datastax.oss.driver.api.core.auth.Authenticator com.datastax.oss.driver.api.core.auth.AuthProvider::newAuthenticator(===java.net.SocketAddress===, java.lang.String) throws com.datastax.oss.driver.api.core.auth.AuthenticationException", - "new": "parameter com.datastax.oss.driver.api.core.auth.Authenticator com.datastax.oss.driver.api.core.auth.AuthProvider::newAuthenticator(===com.datastax.oss.driver.api.core.metadata.EndPoint===, java.lang.String) throws com.datastax.oss.driver.api.core.auth.AuthenticationException", - "justification": "JAVA-2165: Abstract node connection information" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.annotation.JsonFormat.Shape.BOOLEAN", + "new": "field com.fasterxml.jackson.annotation.JsonFormat.Shape.BOOLEAN", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.method.parameterTypeChanged", - "old": "parameter void com.datastax.oss.driver.api.core.auth.AuthProvider::onMissingChallenge(===java.net.SocketAddress===) throws com.datastax.oss.driver.api.core.auth.AuthenticationException", - "new": "parameter void com.datastax.oss.driver.api.core.auth.AuthProvider::onMissingChallenge(===com.datastax.oss.driver.api.core.metadata.EndPoint===) throws com.datastax.oss.driver.api.core.auth.AuthenticationException", - "justification": "JAVA-2165: Abstract node connection information" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.annotation.JsonFormat.Shape.NATURAL", + "new": "field com.fasterxml.jackson.annotation.JsonFormat.Shape.NATURAL", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.method.parameterTypeChanged", - "old": "parameter void com.datastax.oss.driver.api.core.auth.AuthenticationException::(===java.net.SocketAddress===, java.lang.String)", - "new": "parameter void com.datastax.oss.driver.api.core.auth.AuthenticationException::(===com.datastax.oss.driver.api.core.metadata.EndPoint===, java.lang.String)", - "justification": "JAVA-2165: Abstract node connection information" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.annotation.JsonFormat.Shape.NUMBER", + "new": "field com.fasterxml.jackson.annotation.JsonFormat.Shape.NUMBER", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.method.parameterTypeChanged", - "old": "parameter void com.datastax.oss.driver.api.core.auth.AuthenticationException::(===java.net.SocketAddress===, java.lang.String, java.lang.Throwable)", - "new": "parameter void com.datastax.oss.driver.api.core.auth.AuthenticationException::(===com.datastax.oss.driver.api.core.metadata.EndPoint===, java.lang.String, java.lang.Throwable)", - "justification": "JAVA-2165: Abstract node connection information" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.annotation.JsonFormat.Shape.NUMBER_FLOAT", + "new": "field com.fasterxml.jackson.annotation.JsonFormat.Shape.NUMBER_FLOAT", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.method.removed", - "old": "method java.net.SocketAddress com.datastax.oss.driver.api.core.auth.AuthenticationException::getAddress()", - "justification": "JAVA-2165: Abstract node connection information" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.annotation.JsonFormat.Shape.NUMBER_INT", + "new": "field com.fasterxml.jackson.annotation.JsonFormat.Shape.NUMBER_INT", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.method.numberOfParametersChanged", - "old": "method void com.datastax.oss.driver.api.core.loadbalancing.LoadBalancingPolicy::init(java.util.Map, com.datastax.oss.driver.api.core.loadbalancing.LoadBalancingPolicy.DistanceReporter, java.util.Set)", - "new": "method void com.datastax.oss.driver.api.core.loadbalancing.LoadBalancingPolicy::init(java.util.Map, com.datastax.oss.driver.api.core.loadbalancing.LoadBalancingPolicy.DistanceReporter)", - "justification": "JAVA-2165: Abstract node connection information" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.annotation.JsonFormat.Shape.OBJECT", + "new": "field com.fasterxml.jackson.annotation.JsonFormat.Shape.OBJECT", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.Map com.datastax.oss.driver.api.core.metadata.Metadata::getNodes()", - "new": "method java.util.Map com.datastax.oss.driver.api.core.metadata.Metadata::getNodes()", - "justification": "JAVA-2165: Abstract node connection information" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.annotation.JsonFormat.Shape.SCALAR", + "new": "field com.fasterxml.jackson.annotation.JsonFormat.Shape.SCALAR", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.method.addedToInterface", - "new": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.Node::getBroadcastRpcAddress()", - "justification": "JAVA-2165: Abstract node connection information" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.annotation.JsonFormat.Shape.STRING", + "new": "field com.fasterxml.jackson.annotation.JsonFormat.Shape.STRING", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { + "ignore": true, "code": "java.method.removed", - "old": "method java.net.InetSocketAddress com.datastax.oss.driver.api.core.metadata.Node::getConnectAddress()", - "justification": "JAVA-2165: Abstract node connection information" - }, - { - "code": "java.method.addedToInterface", - "new": "method com.datastax.oss.driver.api.core.metadata.EndPoint com.datastax.oss.driver.api.core.metadata.Node::getEndPoint()", - "package": "com.datastax.oss.driver.api.core.metadata", - "justification": "JAVA-2165: Abstract node connection information" - }, - { - "code": "java.method.parameterTypeChanged", - "old": "parameter javax.net.ssl.SSLEngine com.datastax.oss.driver.api.core.ssl.SslEngineFactory::newSslEngine(===java.net.SocketAddress===)", - "new": "parameter javax.net.ssl.SSLEngine com.datastax.oss.driver.api.core.ssl.SslEngineFactory::newSslEngine(===com.datastax.oss.driver.api.core.metadata.EndPoint===)", - "justification": "JAVA-2165: Abstract node connection information" - }, - { - "code": "java.method.addedToInterface", - "new": "method long com.datastax.oss.driver.api.core.cql.Statement>>::getQueryTimestamp()", - "justification": "JAVA-2143: Rename Statement.setTimestamp() to setQueryTimestamp()" + "old": "method void com.fasterxml.jackson.annotation.JsonFormat.Value::(java.lang.String, com.fasterxml.jackson.annotation.JsonFormat.Shape, java.util.Locale, java.util.TimeZone, com.fasterxml.jackson.annotation.JsonFormat.Features)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { + "ignore": true, "code": "java.method.removed", - "old": "method long com.datastax.oss.driver.api.core.cql.Statement>>::getTimestamp()", - "justification": "JAVA-2143: Rename Statement.setTimestamp() to setQueryTimestamp()" - }, - { - "code": "java.method.addedToInterface", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setQueryTimestamp(long)", - "justification": "JAVA-2143: Rename Statement.setTimestamp() to setQueryTimestamp()" + "old": "method void com.fasterxml.jackson.annotation.JsonFormat.Value::(java.lang.String, com.fasterxml.jackson.annotation.JsonFormat.Shape, java.util.Locale, java.lang.String, java.util.TimeZone, com.fasterxml.jackson.annotation.JsonFormat.Features)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { + "ignore": true, "code": "java.method.removed", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setTimestamp(long)", - "justification": "JAVA-2143: Rename Statement.setTimestamp() to setQueryTimestamp()" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::copy(java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.BatchStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::copy(java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.BatchStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setConsistencyLevel(com.datastax.oss.driver.api.core.ConsistencyLevel) @ com.datastax.oss.driver.api.core.cql.BatchStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setConsistencyLevel(com.datastax.oss.driver.api.core.ConsistencyLevel) @ com.datastax.oss.driver.api.core.cql.BatchStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setCustomPayload(java.util.Map) @ com.datastax.oss.driver.api.core.cql.BatchStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setCustomPayload(java.util.Map) @ com.datastax.oss.driver.api.core.cql.BatchStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setExecutionProfile(com.datastax.oss.driver.api.core.config.DriverExecutionProfile) @ com.datastax.oss.driver.api.core.cql.BatchStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setExecutionProfile(com.datastax.oss.driver.api.core.config.DriverExecutionProfile) @ com.datastax.oss.driver.api.core.cql.BatchStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setExecutionProfileName(java.lang.String) @ com.datastax.oss.driver.api.core.cql.BatchStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setExecutionProfileName(java.lang.String) @ com.datastax.oss.driver.api.core.cql.BatchStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setIdempotent(java.lang.Boolean) @ com.datastax.oss.driver.api.core.cql.BatchStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setIdempotent(java.lang.Boolean) @ com.datastax.oss.driver.api.core.cql.BatchStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setNode(com.datastax.oss.driver.api.core.metadata.Node) @ com.datastax.oss.driver.api.core.cql.BatchStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setNode(com.datastax.oss.driver.api.core.metadata.Node) @ com.datastax.oss.driver.api.core.cql.BatchStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setPageSize(int) @ com.datastax.oss.driver.api.core.cql.BatchStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setPageSize(int) @ com.datastax.oss.driver.api.core.cql.BatchStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setPagingState(java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.BatchStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setPagingState(java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.BatchStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingKey(java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.BatchStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingKey(java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.BatchStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingKey(java.nio.ByteBuffer[]) @ com.datastax.oss.driver.api.core.cql.BatchStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingKey(java.nio.ByteBuffer[]) @ com.datastax.oss.driver.api.core.cql.BatchStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingKeyspace(com.datastax.oss.driver.api.core.CqlIdentifier) @ com.datastax.oss.driver.api.core.cql.BatchStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingKeyspace(com.datastax.oss.driver.api.core.CqlIdentifier) @ com.datastax.oss.driver.api.core.cql.BatchStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingKeyspace(java.lang.String) @ com.datastax.oss.driver.api.core.cql.BatchStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingKeyspace(java.lang.String) @ com.datastax.oss.driver.api.core.cql.BatchStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingToken(com.datastax.oss.driver.api.core.metadata.token.Token) @ com.datastax.oss.driver.api.core.cql.BatchStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingToken(com.datastax.oss.driver.api.core.metadata.token.Token) @ com.datastax.oss.driver.api.core.cql.BatchStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setSerialConsistencyLevel(com.datastax.oss.driver.api.core.ConsistencyLevel) @ com.datastax.oss.driver.api.core.cql.BatchStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setSerialConsistencyLevel(com.datastax.oss.driver.api.core.ConsistencyLevel) @ com.datastax.oss.driver.api.core.cql.BatchStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setTimeout(java.time.Duration) @ com.datastax.oss.driver.api.core.cql.BatchStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setTimeout(java.time.Duration) @ com.datastax.oss.driver.api.core.cql.BatchStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setTracing(boolean) @ com.datastax.oss.driver.api.core.cql.BatchStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setTracing(boolean) @ com.datastax.oss.driver.api.core.cql.BatchStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::copy(java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.BatchableStatement>>", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::copy(java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.BatchableStatement>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setConsistencyLevel(com.datastax.oss.driver.api.core.ConsistencyLevel) @ com.datastax.oss.driver.api.core.cql.BatchableStatement>>", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setConsistencyLevel(com.datastax.oss.driver.api.core.ConsistencyLevel) @ com.datastax.oss.driver.api.core.cql.BatchableStatement>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setCustomPayload(java.util.Map) @ com.datastax.oss.driver.api.core.cql.BatchableStatement>>", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setCustomPayload(java.util.Map) @ com.datastax.oss.driver.api.core.cql.BatchableStatement>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setExecutionProfile(com.datastax.oss.driver.api.core.config.DriverExecutionProfile) @ com.datastax.oss.driver.api.core.cql.BatchableStatement>>", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setExecutionProfile(com.datastax.oss.driver.api.core.config.DriverExecutionProfile) @ com.datastax.oss.driver.api.core.cql.BatchableStatement>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setExecutionProfileName(java.lang.String) @ com.datastax.oss.driver.api.core.cql.BatchableStatement>>", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setExecutionProfileName(java.lang.String) @ com.datastax.oss.driver.api.core.cql.BatchableStatement>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setIdempotent(java.lang.Boolean) @ com.datastax.oss.driver.api.core.cql.BatchableStatement>>", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setIdempotent(java.lang.Boolean) @ com.datastax.oss.driver.api.core.cql.BatchableStatement>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setNode(com.datastax.oss.driver.api.core.metadata.Node) @ com.datastax.oss.driver.api.core.cql.BatchableStatement>>", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setNode(com.datastax.oss.driver.api.core.metadata.Node) @ com.datastax.oss.driver.api.core.cql.BatchableStatement>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setPageSize(int) @ com.datastax.oss.driver.api.core.cql.BatchableStatement>>", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setPageSize(int) @ com.datastax.oss.driver.api.core.cql.BatchableStatement>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setPagingState(java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.BatchableStatement>>", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setPagingState(java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.BatchableStatement>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingKey(java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.BatchableStatement>>", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingKey(java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.BatchableStatement>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingKey(java.nio.ByteBuffer[]) @ com.datastax.oss.driver.api.core.cql.BatchableStatement>>", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingKey(java.nio.ByteBuffer[]) @ com.datastax.oss.driver.api.core.cql.BatchableStatement>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingKeyspace(com.datastax.oss.driver.api.core.CqlIdentifier) @ com.datastax.oss.driver.api.core.cql.BatchableStatement>>", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingKeyspace(com.datastax.oss.driver.api.core.CqlIdentifier) @ com.datastax.oss.driver.api.core.cql.BatchableStatement>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingKeyspace(java.lang.String) @ com.datastax.oss.driver.api.core.cql.BatchableStatement>>", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingKeyspace(java.lang.String) @ com.datastax.oss.driver.api.core.cql.BatchableStatement>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingToken(com.datastax.oss.driver.api.core.metadata.token.Token) @ com.datastax.oss.driver.api.core.cql.BatchableStatement>>", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingToken(com.datastax.oss.driver.api.core.metadata.token.Token) @ com.datastax.oss.driver.api.core.cql.BatchableStatement>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setSerialConsistencyLevel(com.datastax.oss.driver.api.core.ConsistencyLevel) @ com.datastax.oss.driver.api.core.cql.BatchableStatement>>", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setSerialConsistencyLevel(com.datastax.oss.driver.api.core.ConsistencyLevel) @ com.datastax.oss.driver.api.core.cql.BatchableStatement>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setTimeout(java.time.Duration) @ com.datastax.oss.driver.api.core.cql.BatchableStatement>>", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setTimeout(java.time.Duration) @ com.datastax.oss.driver.api.core.cql.BatchableStatement>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setTracing(boolean) @ com.datastax.oss.driver.api.core.cql.BatchableStatement>>", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setTracing(boolean) @ com.datastax.oss.driver.api.core.cql.BatchableStatement>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::set(com.datastax.oss.driver.api.core.CqlIdentifier, ValueT, com.datastax.oss.driver.api.core.type.codec.TypeCodec) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::set(com.datastax.oss.driver.api.core.CqlIdentifier, ValueT, com.datastax.oss.driver.api.core.type.codec.TypeCodec) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::set(com.datastax.oss.driver.api.core.CqlIdentifier, ValueT, com.datastax.oss.driver.api.core.type.reflect.GenericType) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::set(com.datastax.oss.driver.api.core.CqlIdentifier, ValueT, com.datastax.oss.driver.api.core.type.reflect.GenericType) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::set(com.datastax.oss.driver.api.core.CqlIdentifier, ValueT, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::set(com.datastax.oss.driver.api.core.CqlIdentifier, ValueT, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "old": "method void com.fasterxml.jackson.annotation.JsonFormat.Value::(java.lang.String, com.fasterxml.jackson.annotation.JsonFormat.Shape, java.lang.String, java.lang.String, com.fasterxml.jackson.annotation.JsonFormat.Features)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, com.datastax.oss.driver.api.core.type.codec.TypeCodec) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, com.datastax.oss.driver.api.core.type.codec.TypeCodec) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.annotation.PropertyAccessor.ALL", + "new": "field com.fasterxml.jackson.annotation.PropertyAccessor.ALL", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, com.datastax.oss.driver.api.core.type.reflect.GenericType) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, com.datastax.oss.driver.api.core.type.reflect.GenericType) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.annotation.PropertyAccessor.CREATOR", + "new": "field com.fasterxml.jackson.annotation.PropertyAccessor.CREATOR", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.annotation.PropertyAccessor.FIELD", + "new": "field com.fasterxml.jackson.annotation.PropertyAccessor.FIELD", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::set(java.lang.String, ValueT, com.datastax.oss.driver.api.core.type.codec.TypeCodec) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::set(java.lang.String, ValueT, com.datastax.oss.driver.api.core.type.codec.TypeCodec) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.annotation.PropertyAccessor.IS_GETTER", + "new": "field com.fasterxml.jackson.annotation.PropertyAccessor.IS_GETTER", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::set(java.lang.String, ValueT, com.datastax.oss.driver.api.core.type.reflect.GenericType) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::set(java.lang.String, ValueT, com.datastax.oss.driver.api.core.type.reflect.GenericType) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.annotation.PropertyAccessor.NONE", + "new": "field com.fasterxml.jackson.annotation.PropertyAccessor.NONE", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::set(java.lang.String, ValueT, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::set(java.lang.String, ValueT, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.serialVersionUIDUnchanged", + "old": "field com.fasterxml.jackson.core.JsonFactory.serialVersionUID", + "new": "field com.fasterxml.jackson.core.JsonFactory.serialVersionUID", + "serialVersionUID": "2", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setBigDecimal(com.datastax.oss.driver.api.core.CqlIdentifier, java.math.BigDecimal) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setBigDecimal(com.datastax.oss.driver.api.core.CqlIdentifier, java.math.BigDecimal) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.finalMethodAddedToNonFinalClass", + "new": "method int com.fasterxml.jackson.core.JsonFactory::getFactoryFeatures()", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBigDecimal(int, java.math.BigDecimal) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBigDecimal(int, java.math.BigDecimal) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER", + "new": "field com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setBigDecimal(java.lang.String, java.math.BigDecimal) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setBigDecimal(java.lang.String, java.math.BigDecimal) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_LEADING_DECIMAL_POINT_FOR_NUMBERS", + "new": "field com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_LEADING_DECIMAL_POINT_FOR_NUMBERS", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setBigInteger(com.datastax.oss.driver.api.core.CqlIdentifier, java.math.BigInteger) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setBigInteger(com.datastax.oss.driver.api.core.CqlIdentifier, java.math.BigInteger) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_MISSING_VALUES", + "new": "field com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_MISSING_VALUES", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBigInteger(int, java.math.BigInteger) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBigInteger(int, java.math.BigInteger) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS", + "new": "field com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setBigInteger(java.lang.String, java.math.BigInteger) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setBigInteger(java.lang.String, java.math.BigInteger) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS", + "new": "field com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setBoolean(com.datastax.oss.driver.api.core.CqlIdentifier, boolean) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setBoolean(com.datastax.oss.driver.api.core.CqlIdentifier, boolean) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_TRAILING_COMMA", + "new": "field com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_TRAILING_COMMA", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBoolean(int, boolean) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBoolean(int, boolean) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.core.JsonParser.Feature.IGNORE_UNDEFINED", + "new": "field com.fasterxml.jackson.core.JsonParser.Feature.IGNORE_UNDEFINED", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setBoolean(java.lang.String, boolean) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setBoolean(java.lang.String, boolean) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.core.JsonParser.Feature.INCLUDE_SOURCE_IN_LOCATION", + "new": "field com.fasterxml.jackson.core.JsonParser.Feature.INCLUDE_SOURCE_IN_LOCATION", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setByte(com.datastax.oss.driver.api.core.CqlIdentifier, byte) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setByte(com.datastax.oss.driver.api.core.CqlIdentifier, byte) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.core.JsonParser.Feature.STRICT_DUPLICATE_DETECTION", + "new": "field com.fasterxml.jackson.core.JsonParser.Feature.STRICT_DUPLICATE_DETECTION", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setByte(int, byte) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setByte(int, byte) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setByte(java.lang.String, byte) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setByte(java.lang.String, byte) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setByteBuffer(com.datastax.oss.driver.api.core.CqlIdentifier, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setByteBuffer(com.datastax.oss.driver.api.core.CqlIdentifier, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setByteBuffer(int, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setByteBuffer(int, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setByteBuffer(java.lang.String, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setByteBuffer(java.lang.String, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setBytesUnsafe(com.datastax.oss.driver.api.core.CqlIdentifier, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setBytesUnsafe(com.datastax.oss.driver.api.core.CqlIdentifier, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBytesUnsafe(int, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBytesUnsafe(int, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setBytesUnsafe(java.lang.String, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setBytesUnsafe(java.lang.String, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setCqlDuration(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.data.CqlDuration) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setCqlDuration(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.data.CqlDuration) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setCqlDuration(int, com.datastax.oss.driver.api.core.data.CqlDuration) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setCqlDuration(int, com.datastax.oss.driver.api.core.data.CqlDuration) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setCqlDuration(java.lang.String, com.datastax.oss.driver.api.core.data.CqlDuration) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setCqlDuration(java.lang.String, com.datastax.oss.driver.api.core.data.CqlDuration) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setDouble(com.datastax.oss.driver.api.core.CqlIdentifier, double) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setDouble(com.datastax.oss.driver.api.core.CqlIdentifier, double) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setDouble(int, double) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setDouble(int, double) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setDouble(java.lang.String, double) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setDouble(java.lang.String, double) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setFloat(com.datastax.oss.driver.api.core.CqlIdentifier, float) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setFloat(com.datastax.oss.driver.api.core.CqlIdentifier, float) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setFloat(int, float) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setFloat(int, float) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setFloat(java.lang.String, float) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setFloat(java.lang.String, float) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setInetAddress(com.datastax.oss.driver.api.core.CqlIdentifier, java.net.InetAddress) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setInetAddress(com.datastax.oss.driver.api.core.CqlIdentifier, java.net.InetAddress) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInetAddress(int, java.net.InetAddress) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInetAddress(int, java.net.InetAddress) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setInetAddress(java.lang.String, java.net.InetAddress) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setInetAddress(java.lang.String, java.net.InetAddress) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setInstant(com.datastax.oss.driver.api.core.CqlIdentifier, java.time.Instant) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setInstant(com.datastax.oss.driver.api.core.CqlIdentifier, java.time.Instant) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInstant(int, java.time.Instant) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInstant(int, java.time.Instant) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setInstant(java.lang.String, java.time.Instant) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setInstant(java.lang.String, java.time.Instant) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setInt(com.datastax.oss.driver.api.core.CqlIdentifier, int) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setInt(com.datastax.oss.driver.api.core.CqlIdentifier, int) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInt(int, int) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInt(int, int) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setInt(java.lang.String, int) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setInt(java.lang.String, int) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setList(com.datastax.oss.driver.api.core.CqlIdentifier, java.util.List, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setList(com.datastax.oss.driver.api.core.CqlIdentifier, java.util.List, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setList(int, java.util.List, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setList(int, java.util.List, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setList(java.lang.String, java.util.List, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setList(java.lang.String, java.util.List, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setLocalDate(com.datastax.oss.driver.api.core.CqlIdentifier, java.time.LocalDate) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setLocalDate(com.datastax.oss.driver.api.core.CqlIdentifier, java.time.LocalDate) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLocalDate(int, java.time.LocalDate) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLocalDate(int, java.time.LocalDate) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setLocalDate(java.lang.String, java.time.LocalDate) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setLocalDate(java.lang.String, java.time.LocalDate) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setLocalTime(com.datastax.oss.driver.api.core.CqlIdentifier, java.time.LocalTime) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setLocalTime(com.datastax.oss.driver.api.core.CqlIdentifier, java.time.LocalTime) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLocalTime(int, java.time.LocalTime) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLocalTime(int, java.time.LocalTime) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setLocalTime(java.lang.String, java.time.LocalTime) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setLocalTime(java.lang.String, java.time.LocalTime) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.numberOfParametersChanged", + "old": "method void com.fasterxml.jackson.core.JsonPointer::(java.lang.String, java.lang.String, com.fasterxml.jackson.core.JsonPointer)", + "new": "method void com.fasterxml.jackson.core.JsonPointer::(com.fasterxml.jackson.core.JsonPointer, com.fasterxml.jackson.core.JsonPointer)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setLong(com.datastax.oss.driver.api.core.CqlIdentifier, long) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setLong(com.datastax.oss.driver.api.core.CqlIdentifier, long) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.numberOfParametersChanged", + "old": "method void com.fasterxml.jackson.core.JsonPointer::(java.lang.String, java.lang.String, int, com.fasterxml.jackson.core.JsonPointer)", + "new": "method void com.fasterxml.jackson.core.JsonPointer::(com.fasterxml.jackson.core.JsonPointer, java.lang.String, int)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLong(int, long) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLong(int, long) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method com.fasterxml.jackson.core.JsonPointer com.fasterxml.jackson.core.JsonPointer::_constructHead(int, com.fasterxml.jackson.core.JsonPointer)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setLong(java.lang.String, long) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setLong(java.lang.String, long) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method com.fasterxml.jackson.core.JsonPointer com.fasterxml.jackson.core.JsonPointer::_parseQuotedTail(java.lang.String, int)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setMap(com.datastax.oss.driver.api.core.CqlIdentifier, java.util.Map, java.lang.Class, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setMap(com.datastax.oss.driver.api.core.CqlIdentifier, java.util.Map, java.lang.Class, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.finalMethodAddedToNonFinalClass", + "new": "method int com.fasterxml.jackson.core.JsonStreamContext::getNestingDepth()", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setMap(int, java.util.Map, java.lang.Class, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setMap(int, java.util.Map, java.lang.Class, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.core.StreamReadFeature.IGNORE_UNDEFINED", + "new": "field com.fasterxml.jackson.core.StreamReadFeature.IGNORE_UNDEFINED", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setMap(java.lang.String, java.util.Map, java.lang.Class, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setMap(java.lang.String, java.util.Map, java.lang.Class, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.core.StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION", + "new": "field com.fasterxml.jackson.core.StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setSet(com.datastax.oss.driver.api.core.CqlIdentifier, java.util.Set, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setSet(com.datastax.oss.driver.api.core.CqlIdentifier, java.util.Set, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.core.StreamReadFeature.STRICT_DUPLICATE_DETECTION", + "new": "field com.fasterxml.jackson.core.StreamReadFeature.STRICT_DUPLICATE_DETECTION", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setSet(int, java.util.Set, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setSet(int, java.util.Set, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.core.StreamWriteFeature.AUTO_CLOSE_CONTENT", + "new": "field com.fasterxml.jackson.core.StreamWriteFeature.AUTO_CLOSE_CONTENT", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setSet(java.lang.String, java.util.Set, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setSet(java.lang.String, java.util.Set, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.core.StreamWriteFeature.AUTO_CLOSE_TARGET", + "new": "field com.fasterxml.jackson.core.StreamWriteFeature.AUTO_CLOSE_TARGET", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setShort(com.datastax.oss.driver.api.core.CqlIdentifier, short) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setShort(com.datastax.oss.driver.api.core.CqlIdentifier, short) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.core.StreamWriteFeature.IGNORE_UNKNOWN", + "new": "field com.fasterxml.jackson.core.StreamWriteFeature.IGNORE_UNKNOWN", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setShort(int, short) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setShort(int, short) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.core.StreamWriteFeature.STRICT_DUPLICATE_DETECTION", + "new": "field com.fasterxml.jackson.core.StreamWriteFeature.STRICT_DUPLICATE_DETECTION", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setShort(java.lang.String, short) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setShort(java.lang.String, short) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.serialVersionUIDUnchanged", + "old": "field com.fasterxml.jackson.core.io.ContentReference.serialVersionUID", + "new": "field com.fasterxml.jackson.core.io.ContentReference.serialVersionUID", + "serialVersionUID": "1", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setString(com.datastax.oss.driver.api.core.CqlIdentifier, java.lang.String) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setString(com.datastax.oss.driver.api.core.CqlIdentifier, java.lang.String) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method int com.fasterxml.jackson.core.io.ContentReference::maxContentSnippetLength()", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setString(int, java.lang.String) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setString(int, java.lang.String) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.core.json.JsonReadFeature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER", + "new": "field com.fasterxml.jackson.core.json.JsonReadFeature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setString(java.lang.String, java.lang.String) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setString(java.lang.String, java.lang.String) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.core.json.JsonReadFeature.ALLOW_LEADING_ZEROS_FOR_NUMBERS", + "new": "field com.fasterxml.jackson.core.json.JsonReadFeature.ALLOW_LEADING_ZEROS_FOR_NUMBERS", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setToNull(com.datastax.oss.driver.api.core.CqlIdentifier) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setToNull(com.datastax.oss.driver.api.core.CqlIdentifier) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.core.json.JsonReadFeature.ALLOW_MISSING_VALUES", + "new": "field com.fasterxml.jackson.core.json.JsonReadFeature.ALLOW_MISSING_VALUES", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setToNull(int) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setToNull(int) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.core.json.JsonReadFeature.ALLOW_NON_NUMERIC_NUMBERS", + "new": "field com.fasterxml.jackson.core.json.JsonReadFeature.ALLOW_NON_NUMERIC_NUMBERS", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setToNull(java.lang.String) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setToNull(java.lang.String) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.core.json.JsonReadFeature.ALLOW_SINGLE_QUOTES", + "new": "field com.fasterxml.jackson.core.json.JsonReadFeature.ALLOW_SINGLE_QUOTES", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setToken(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.metadata.token.Token) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setToken(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.metadata.token.Token) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.core.json.JsonReadFeature.ALLOW_TRAILING_COMMA", + "new": "field com.fasterxml.jackson.core.json.JsonReadFeature.ALLOW_TRAILING_COMMA", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setToken(int, com.datastax.oss.driver.api.core.metadata.token.Token) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setToken(int, com.datastax.oss.driver.api.core.metadata.token.Token) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.core.json.JsonReadFeature.ALLOW_UNQUOTED_FIELD_NAMES", + "new": "field com.fasterxml.jackson.core.json.JsonReadFeature.ALLOW_UNQUOTED_FIELD_NAMES", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setToken(java.lang.String, com.datastax.oss.driver.api.core.metadata.token.Token) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setToken(java.lang.String, com.datastax.oss.driver.api.core.metadata.token.Token) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.core.json.JsonWriteFeature.ESCAPE_NON_ASCII", + "new": "field com.fasterxml.jackson.core.json.JsonWriteFeature.ESCAPE_NON_ASCII", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setTupleValue(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.data.TupleValue) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setTupleValue(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.data.TupleValue) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.core.json.JsonWriteFeature.QUOTE_FIELD_NAMES", + "new": "field com.fasterxml.jackson.core.json.JsonWriteFeature.QUOTE_FIELD_NAMES", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setTupleValue(int, com.datastax.oss.driver.api.core.data.TupleValue) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setTupleValue(int, com.datastax.oss.driver.api.core.data.TupleValue) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.core.json.JsonWriteFeature.WRITE_NAN_AS_STRINGS", + "new": "field com.fasterxml.jackson.core.json.JsonWriteFeature.WRITE_NAN_AS_STRINGS", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setTupleValue(java.lang.String, com.datastax.oss.driver.api.core.data.TupleValue) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setTupleValue(java.lang.String, com.datastax.oss.driver.api.core.data.TupleValue) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.core.json.JsonWriteFeature.WRITE_NUMBERS_AS_STRINGS", + "new": "field com.fasterxml.jackson.core.json.JsonWriteFeature.WRITE_NUMBERS_AS_STRINGS", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setUdtValue(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.data.UdtValue) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setUdtValue(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.data.UdtValue) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.removed", + "old": "field com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer._intern", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setUdtValue(int, com.datastax.oss.driver.api.core.data.UdtValue) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setUdtValue(int, com.datastax.oss.driver.api.core.data.UdtValue) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.exception.checkedAdded", + "old": "method void com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer::_reportTooManyCollisions()", + "new": "method void com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer::_reportTooManyCollisions() throws com.fasterxml.jackson.core.exc.StreamConstraintsException", + "exception": "com.fasterxml.jackson.core.exc.StreamConstraintsException", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setUdtValue(java.lang.String, com.datastax.oss.driver.api.core.data.UdtValue) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setUdtValue(java.lang.String, com.datastax.oss.driver.api.core.data.UdtValue) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.exception.checkedAdded", + "old": "method java.lang.String com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer::addName(java.lang.String, int)", + "new": "method java.lang.String com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer::addName(java.lang.String, int) throws com.fasterxml.jackson.core.exc.StreamConstraintsException", + "exception": "com.fasterxml.jackson.core.exc.StreamConstraintsException", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setUuid(com.datastax.oss.driver.api.core.CqlIdentifier, java.util.UUID) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setUuid(com.datastax.oss.driver.api.core.CqlIdentifier, java.util.UUID) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.exception.checkedAdded", + "old": "method java.lang.String com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer::addName(java.lang.String, int, int)", + "new": "method java.lang.String com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer::addName(java.lang.String, int, int) throws com.fasterxml.jackson.core.exc.StreamConstraintsException", + "exception": "com.fasterxml.jackson.core.exc.StreamConstraintsException", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setUuid(int, java.util.UUID) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setUuid(int, java.util.UUID) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.exception.checkedAdded", + "old": "method java.lang.String com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer::addName(java.lang.String, int, int, int)", + "new": "method java.lang.String com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer::addName(java.lang.String, int, int, int) throws com.fasterxml.jackson.core.exc.StreamConstraintsException", + "exception": "com.fasterxml.jackson.core.exc.StreamConstraintsException", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setUuid(java.lang.String, java.util.UUID) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setUuid(java.lang.String, java.util.UUID) @ com.datastax.oss.driver.api.core.cql.Bindable>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.exception.checkedAdded", + "old": "method java.lang.String com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer::addName(java.lang.String, int[], int)", + "new": "method java.lang.String com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer::addName(java.lang.String, int[], int) throws com.fasterxml.jackson.core.exc.StreamConstraintsException", + "exception": "com.fasterxml.jackson.core.exc.StreamConstraintsException", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Bindable>>::unset(com.datastax.oss.driver.api.core.CqlIdentifier)", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Bindable>>::unset(com.datastax.oss.driver.api.core.CqlIdentifier)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.removed", + "old": "field com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer._flags", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Bindable>>::unset(int)", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Bindable>>::unset(int)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.exception.checkedAdded", + "old": "method void com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer::_reportTooManyCollisions(int)", + "new": "method void com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer::_reportTooManyCollisions(int) throws com.fasterxml.jackson.core.exc.StreamConstraintsException", + "exception": "com.fasterxml.jackson.core.exc.StreamConstraintsException", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Bindable>>::unset(java.lang.String)", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Bindable>>::unset(java.lang.String)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.visibilityIncreased", + "old": "method com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer::createRoot(int)", + "new": "method com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer::createRoot(int)", + "oldVisibility": "protected", + "newVisibility": "public", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::copy(java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::copy(java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.exception.checkedAdded", + "old": "method java.lang.String com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer::findSymbol(char[], int, int, int)", + "new": "method java.lang.String com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer::findSymbol(char[], int, int, int) throws java.io.IOException", + "exception": "java.io.IOException", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::set(com.datastax.oss.driver.api.core.CqlIdentifier, ValueT, com.datastax.oss.driver.api.core.type.codec.TypeCodec) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::set(com.datastax.oss.driver.api.core.CqlIdentifier, ValueT, com.datastax.oss.driver.api.core.type.codec.TypeCodec) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.serialVersionUIDUnchanged", + "old": "field com.fasterxml.jackson.core.util.Separators.serialVersionUID", + "new": "field com.fasterxml.jackson.core.util.Separators.serialVersionUID", + "serialVersionUID": "1", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::set(com.datastax.oss.driver.api.core.CqlIdentifier, ValueT, com.datastax.oss.driver.api.core.type.reflect.GenericType) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::set(com.datastax.oss.driver.api.core.CqlIdentifier, ValueT, com.datastax.oss.driver.api.core.type.reflect.GenericType) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.exception.checkedAdded", + "old": "method void com.fasterxml.jackson.core.util.TextBuffer::append(char)", + "new": "method void com.fasterxml.jackson.core.util.TextBuffer::append(char) throws java.io.IOException", + "exception": "java.io.IOException", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::set(com.datastax.oss.driver.api.core.CqlIdentifier, ValueT, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::set(com.datastax.oss.driver.api.core.CqlIdentifier, ValueT, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.exception.checkedAdded", + "old": "method void com.fasterxml.jackson.core.util.TextBuffer::append(char[], int, int)", + "new": "method void com.fasterxml.jackson.core.util.TextBuffer::append(char[], int, int) throws java.io.IOException", + "exception": "java.io.IOException", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, com.datastax.oss.driver.api.core.type.codec.TypeCodec) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, com.datastax.oss.driver.api.core.type.codec.TypeCodec) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.exception.checkedAdded", + "old": "method void com.fasterxml.jackson.core.util.TextBuffer::append(java.lang.String, int, int)", + "new": "method void com.fasterxml.jackson.core.util.TextBuffer::append(java.lang.String, int, int) throws java.io.IOException", + "exception": "java.io.IOException", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, com.datastax.oss.driver.api.core.type.reflect.GenericType) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, com.datastax.oss.driver.api.core.type.reflect.GenericType) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.exception.checkedAdded", + "old": "method char[] com.fasterxml.jackson.core.util.TextBuffer::contentsAsArray()", + "new": "method char[] com.fasterxml.jackson.core.util.TextBuffer::contentsAsArray() throws java.io.IOException", + "exception": "java.io.IOException", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.exception.checkedAdded", + "old": "method java.lang.String com.fasterxml.jackson.core.util.TextBuffer::contentsAsString()", + "new": "method java.lang.String com.fasterxml.jackson.core.util.TextBuffer::contentsAsString() throws java.io.IOException", + "exception": "java.io.IOException", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::set(java.lang.String, ValueT, com.datastax.oss.driver.api.core.type.codec.TypeCodec) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::set(java.lang.String, ValueT, com.datastax.oss.driver.api.core.type.codec.TypeCodec) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.exception.checkedAdded", + "old": "method char[] com.fasterxml.jackson.core.util.TextBuffer::finishCurrentSegment()", + "new": "method char[] com.fasterxml.jackson.core.util.TextBuffer::finishCurrentSegment() throws java.io.IOException", + "exception": "java.io.IOException", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::set(java.lang.String, ValueT, com.datastax.oss.driver.api.core.type.reflect.GenericType) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::set(java.lang.String, ValueT, com.datastax.oss.driver.api.core.type.reflect.GenericType) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.exception.checkedAdded", + "old": "method char[] com.fasterxml.jackson.core.util.TextBuffer::getTextBuffer()", + "new": "method char[] com.fasterxml.jackson.core.util.TextBuffer::getTextBuffer() throws java.io.IOException", + "exception": "java.io.IOException", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::set(java.lang.String, ValueT, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::set(java.lang.String, ValueT, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.exception.checkedAdded", + "old": "method void com.fasterxml.jackson.core.util.TextBuffer::resetWithCopy(char[], int, int)", + "new": "method void com.fasterxml.jackson.core.util.TextBuffer::resetWithCopy(char[], int, int) throws java.io.IOException", + "exception": "java.io.IOException", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setBigDecimal(com.datastax.oss.driver.api.core.CqlIdentifier, java.math.BigDecimal) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setBigDecimal(com.datastax.oss.driver.api.core.CqlIdentifier, java.math.BigDecimal) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.exception.checkedAdded", + "old": "method void com.fasterxml.jackson.core.util.TextBuffer::resetWithCopy(java.lang.String, int, int)", + "new": "method void com.fasterxml.jackson.core.util.TextBuffer::resetWithCopy(java.lang.String, int, int) throws java.io.IOException", + "exception": "java.io.IOException", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBigDecimal(int, java.math.BigDecimal) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBigDecimal(int, java.math.BigDecimal) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.exception.checkedAdded", + "old": "method void com.fasterxml.jackson.core.util.TextBuffer::resetWithString(java.lang.String)", + "new": "method void com.fasterxml.jackson.core.util.TextBuffer::resetWithString(java.lang.String) throws java.io.IOException", + "exception": "java.io.IOException", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setBigDecimal(java.lang.String, java.math.BigDecimal) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setBigDecimal(java.lang.String, java.math.BigDecimal) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.exception.checkedAdded", + "old": "method java.lang.String com.fasterxml.jackson.core.util.TextBuffer::setCurrentAndReturn(int)", + "new": "method java.lang.String com.fasterxml.jackson.core.util.TextBuffer::setCurrentAndReturn(int) throws java.io.IOException", + "exception": "java.io.IOException", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setBigInteger(com.datastax.oss.driver.api.core.CqlIdentifier, java.math.BigInteger) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setBigInteger(com.datastax.oss.driver.api.core.CqlIdentifier, java.math.BigInteger) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method java.lang.Class com.fasterxml.jackson.databind.AnnotationIntrospector::findDeserializationContentType(com.fasterxml.jackson.databind.introspect.Annotated, com.fasterxml.jackson.databind.JavaType)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBigInteger(int, java.math.BigInteger) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBigInteger(int, java.math.BigInteger) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method java.lang.Class com.fasterxml.jackson.databind.AnnotationIntrospector::findDeserializationKeyType(com.fasterxml.jackson.databind.introspect.Annotated, com.fasterxml.jackson.databind.JavaType)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setBigInteger(java.lang.String, java.math.BigInteger) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setBigInteger(java.lang.String, java.math.BigInteger) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method java.lang.Class com.fasterxml.jackson.databind.AnnotationIntrospector::findDeserializationType(com.fasterxml.jackson.databind.introspect.Annotated, com.fasterxml.jackson.databind.JavaType)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setBoolean(com.datastax.oss.driver.api.core.CqlIdentifier, boolean) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setBoolean(com.datastax.oss.driver.api.core.CqlIdentifier, boolean) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method java.lang.Boolean com.fasterxml.jackson.databind.AnnotationIntrospector::findIgnoreUnknownProperties(com.fasterxml.jackson.databind.introspect.AnnotatedClass)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBoolean(int, boolean) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBoolean(int, boolean) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method java.lang.String[] com.fasterxml.jackson.databind.AnnotationIntrospector::findPropertiesToIgnore(com.fasterxml.jackson.databind.introspect.Annotated, boolean)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setBoolean(java.lang.String, boolean) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setBoolean(java.lang.String, boolean) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method java.lang.Class com.fasterxml.jackson.databind.AnnotationIntrospector::findSerializationContentType(com.fasterxml.jackson.databind.introspect.Annotated, com.fasterxml.jackson.databind.JavaType)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setByte(com.datastax.oss.driver.api.core.CqlIdentifier, byte) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setByte(com.datastax.oss.driver.api.core.CqlIdentifier, byte) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method com.fasterxml.jackson.annotation.JsonInclude.Include com.fasterxml.jackson.databind.AnnotationIntrospector::findSerializationInclusion(com.fasterxml.jackson.databind.introspect.Annotated, com.fasterxml.jackson.annotation.JsonInclude.Include)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setByte(int, byte) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setByte(int, byte) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method com.fasterxml.jackson.annotation.JsonInclude.Include com.fasterxml.jackson.databind.AnnotationIntrospector::findSerializationInclusionForContent(com.fasterxml.jackson.databind.introspect.Annotated, com.fasterxml.jackson.annotation.JsonInclude.Include)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setByte(java.lang.String, byte) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setByte(java.lang.String, byte) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method java.lang.Class com.fasterxml.jackson.databind.AnnotationIntrospector::findSerializationKeyType(com.fasterxml.jackson.databind.introspect.Annotated, com.fasterxml.jackson.databind.JavaType)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setByteBuffer(com.datastax.oss.driver.api.core.CqlIdentifier, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setByteBuffer(com.datastax.oss.driver.api.core.CqlIdentifier, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method java.lang.Class com.fasterxml.jackson.databind.AnnotationIntrospector::findSerializationType(com.fasterxml.jackson.databind.introspect.Annotated)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setByteBuffer(int, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setByteBuffer(int, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.class.defaultSerializationChanged", + "old": "class com.fasterxml.jackson.databind.AnnotationIntrospector", + "new": "class com.fasterxml.jackson.databind.AnnotationIntrospector", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setByteBuffer(java.lang.String, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setByteBuffer(java.lang.String, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method com.fasterxml.jackson.databind.type.TypeBindings com.fasterxml.jackson.databind.BeanDescription::bindingsForBeanType()", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setBytesUnsafe(com.datastax.oss.driver.api.core.CqlIdentifier, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setBytesUnsafe(com.datastax.oss.driver.api.core.CqlIdentifier, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method com.fasterxml.jackson.databind.introspect.AnnotatedMethod com.fasterxml.jackson.databind.BeanDescription::findAnySetter()", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBytesUnsafe(int, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBytesUnsafe(int, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method com.fasterxml.jackson.databind.introspect.AnnotatedMember com.fasterxml.jackson.databind.BeanDescription::findAnySetterField()", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setBytesUnsafe(java.lang.String, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setBytesUnsafe(java.lang.String, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method java.util.Map com.fasterxml.jackson.databind.BeanDescription::findBackReferenceProperties()", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setConsistencyLevel(com.datastax.oss.driver.api.core.ConsistencyLevel) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setConsistencyLevel(com.datastax.oss.driver.api.core.ConsistencyLevel) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.abstractMethodAdded", + "new": "method com.fasterxml.jackson.annotation.JsonFormat.Value com.fasterxml.jackson.databind.BeanDescription::findExpectedFormat()", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setCqlDuration(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.data.CqlDuration) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setCqlDuration(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.data.CqlDuration) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method java.lang.reflect.Method com.fasterxml.jackson.databind.BeanDescription::findFactoryMethod(java.lang.Class[])", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setCqlDuration(int, com.datastax.oss.driver.api.core.data.CqlDuration) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setCqlDuration(int, com.datastax.oss.driver.api.core.data.CqlDuration) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method com.fasterxml.jackson.databind.introspect.AnnotatedMethod com.fasterxml.jackson.databind.BeanDescription::findJsonValueMethod()", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setCqlDuration(java.lang.String, com.datastax.oss.driver.api.core.data.CqlDuration) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setCqlDuration(java.lang.String, com.datastax.oss.driver.api.core.data.CqlDuration) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method java.lang.reflect.Constructor com.fasterxml.jackson.databind.BeanDescription::findSingleArgConstructor(java.lang.Class[])", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setCustomPayload(java.util.Map) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setCustomPayload(java.util.Map) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.abstractMethodAdded", + "new": "method com.fasterxml.jackson.databind.introspect.PotentialCreators com.fasterxml.jackson.databind.BeanDescription::getPotentialCreators()", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setDouble(com.datastax.oss.driver.api.core.CqlIdentifier, double) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setDouble(com.datastax.oss.driver.api.core.CqlIdentifier, double) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method com.fasterxml.jackson.databind.JavaType com.fasterxml.jackson.databind.BeanDescription::resolveType(java.lang.reflect.Type)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setDouble(int, double) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setDouble(int, double) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.abstractMethodAdded", + "new": "method com.fasterxml.jackson.databind.cfg.DatatypeFeatures com.fasterxml.jackson.databind.DatabindContext::getDatatypeFeatures()", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setDouble(java.lang.String, double) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setDouble(java.lang.String, double) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.abstractMethodAdded", + "new": "method boolean com.fasterxml.jackson.databind.DatabindContext::isEnabled(com.fasterxml.jackson.databind.cfg.DatatypeFeature)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setExecutionProfile(com.datastax.oss.driver.api.core.config.DriverExecutionProfile) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setExecutionProfile(com.datastax.oss.driver.api.core.config.DriverExecutionProfile) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.abstractMethodAdded", + "new": "method T com.fasterxml.jackson.databind.DatabindContext::reportBadTypeDefinition(com.fasterxml.jackson.databind.BeanDescription, java.lang.String, java.lang.Object[]) throws com.fasterxml.jackson.databind.JsonMappingException", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setExecutionProfileName(java.lang.String) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setExecutionProfileName(java.lang.String) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.serialVersionUIDUnchanged", + "old": "field com.fasterxml.jackson.databind.DeserializationConfig.serialVersionUID", + "new": "field com.fasterxml.jackson.databind.DeserializationConfig.serialVersionUID", + "serialVersionUID": "2", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setFloat(com.datastax.oss.driver.api.core.CqlIdentifier, float) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setFloat(com.datastax.oss.driver.api.core.CqlIdentifier, float) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.numberOfParametersChanged", + "old": "method void com.fasterxml.jackson.databind.DeserializationConfig::(com.fasterxml.jackson.databind.DeserializationConfig, com.fasterxml.jackson.databind.introspect.SimpleMixInResolver, com.fasterxml.jackson.databind.util.RootNameLookup, com.fasterxml.jackson.databind.cfg.ConfigOverrides)", + "new": "method void com.fasterxml.jackson.databind.DeserializationConfig::(com.fasterxml.jackson.databind.DeserializationConfig, com.fasterxml.jackson.databind.cfg.DatatypeFeatures)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setFloat(int, float) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setFloat(int, float) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.numberOfParametersChanged", + "old": "method void com.fasterxml.jackson.databind.DeserializationConfig::(com.fasterxml.jackson.databind.cfg.BaseSettings, com.fasterxml.jackson.databind.jsontype.SubtypeResolver, com.fasterxml.jackson.databind.introspect.SimpleMixInResolver, com.fasterxml.jackson.databind.util.RootNameLookup, com.fasterxml.jackson.databind.cfg.ConfigOverrides)", + "new": "method void com.fasterxml.jackson.databind.DeserializationConfig::(com.fasterxml.jackson.databind.cfg.BaseSettings, com.fasterxml.jackson.databind.jsontype.SubtypeResolver, com.fasterxml.jackson.databind.introspect.SimpleMixInResolver, com.fasterxml.jackson.databind.util.RootNameLookup, com.fasterxml.jackson.databind.cfg.ConfigOverrides, com.fasterxml.jackson.databind.cfg.CoercionConfigs, com.fasterxml.jackson.databind.cfg.DatatypeFeatures)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setFloat(java.lang.String, float) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setFloat(java.lang.String, float) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method void com.fasterxml.jackson.databind.DeserializationConfig::(com.fasterxml.jackson.databind.cfg.BaseSettings, com.fasterxml.jackson.databind.jsontype.SubtypeResolver, com.fasterxml.jackson.databind.introspect.SimpleMixInResolver, com.fasterxml.jackson.databind.util.RootNameLookup, com.fasterxml.jackson.databind.cfg.ConfigOverrides, com.fasterxml.jackson.databind.cfg.CoercionConfigs)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setIdempotent(java.lang.Boolean) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setIdempotent(java.lang.Boolean) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method com.fasterxml.jackson.databind.BeanDescription com.fasterxml.jackson.databind.DeserializationConfig::introspectForBuilder(com.fasterxml.jackson.databind.JavaType)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setInetAddress(com.datastax.oss.driver.api.core.CqlIdentifier, java.net.InetAddress) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setInetAddress(com.datastax.oss.driver.api.core.CqlIdentifier, java.net.InetAddress) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.numberOfParametersChanged", + "old": "method void com.fasterxml.jackson.databind.DeserializationContext::(com.fasterxml.jackson.databind.deser.DeserializerFactory)", + "new": "method void com.fasterxml.jackson.databind.DeserializationContext::(com.fasterxml.jackson.databind.DeserializationContext, com.fasterxml.jackson.databind.deser.DeserializerCache)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInetAddress(int, java.net.InetAddress) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInetAddress(int, java.net.InetAddress) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method com.fasterxml.jackson.databind.JsonMappingException com.fasterxml.jackson.databind.DeserializationContext::endOfInputException(java.lang.Class)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setInetAddress(java.lang.String, java.net.InetAddress) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setInetAddress(java.lang.String, java.net.InetAddress) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.finalMethodAddedToNonFinalClass", + "new": "method java.lang.Object com.fasterxml.jackson.databind.DeserializationContext::findInjectableValue(java.lang.Object, com.fasterxml.jackson.databind.BeanProperty, java.lang.Object, java.lang.Boolean, java.lang.Boolean) throws com.fasterxml.jackson.databind.JsonMappingException", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setInstant(com.datastax.oss.driver.api.core.CqlIdentifier, java.time.Instant) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setInstant(com.datastax.oss.driver.api.core.CqlIdentifier, java.time.Instant) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.finalMethodAddedToNonFinalClass", + "new": "method com.fasterxml.jackson.databind.cfg.DatatypeFeatures com.fasterxml.jackson.databind.DeserializationContext::getDatatypeFeatures()", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInstant(int, java.time.Instant) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInstant(int, java.time.Instant) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method java.text.DateFormat com.fasterxml.jackson.databind.DeserializationContext::getDateFormat()", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setInstant(java.lang.String, java.time.Instant) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setInstant(java.lang.String, java.time.Instant) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.finalMethodAddedToNonFinalClass", + "new": "method boolean com.fasterxml.jackson.databind.DeserializationContext::isEnabled(com.fasterxml.jackson.databind.cfg.DatatypeFeature)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setInt(com.datastax.oss.driver.api.core.CqlIdentifier, int) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setInt(com.datastax.oss.driver.api.core.CqlIdentifier, int) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method com.fasterxml.jackson.databind.JsonMappingException com.fasterxml.jackson.databind.DeserializationContext::mappingException(java.lang.Class)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInt(int, int) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInt(int, int) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method com.fasterxml.jackson.databind.JsonMappingException com.fasterxml.jackson.databind.DeserializationContext::mappingException(java.lang.Class, com.fasterxml.jackson.core.JsonToken)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setInt(java.lang.String, int) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setInt(java.lang.String, int) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method com.fasterxml.jackson.databind.JsonMappingException com.fasterxml.jackson.databind.DeserializationContext::mappingException(java.lang.String)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setList(com.datastax.oss.driver.api.core.CqlIdentifier, java.util.List, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setList(com.datastax.oss.driver.api.core.CqlIdentifier, java.util.List, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method com.fasterxml.jackson.databind.JsonMappingException com.fasterxml.jackson.databind.DeserializationContext::mappingException(java.lang.String, java.lang.Object[])", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setList(int, java.util.List, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setList(int, java.util.List, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method T com.fasterxml.jackson.databind.DeserializationContext::reportBadMerge(com.fasterxml.jackson.databind.JsonDeserializer) throws com.fasterxml.jackson.databind.JsonMappingException", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setList(java.lang.String, java.util.List, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setList(java.lang.String, java.util.List, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method void com.fasterxml.jackson.databind.DeserializationContext::reportMappingException(java.lang.String, java.lang.Object[]) throws com.fasterxml.jackson.databind.JsonMappingException", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setLocalDate(com.datastax.oss.driver.api.core.CqlIdentifier, java.time.LocalDate) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setLocalDate(com.datastax.oss.driver.api.core.CqlIdentifier, java.time.LocalDate) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method void com.fasterxml.jackson.databind.DeserializationContext::reportMissingContent(java.lang.String, java.lang.Object[]) throws com.fasterxml.jackson.databind.JsonMappingException", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLocalDate(int, java.time.LocalDate) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLocalDate(int, java.time.LocalDate) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method void com.fasterxml.jackson.databind.DeserializationContext::reportUnknownProperty(java.lang.Object, java.lang.String, com.fasterxml.jackson.databind.JsonDeserializer) throws com.fasterxml.jackson.databind.JsonMappingException", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setLocalDate(java.lang.String, java.time.LocalDate) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setLocalDate(java.lang.String, java.time.LocalDate) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method void com.fasterxml.jackson.databind.DeserializationContext::reportWrongTokenException(com.fasterxml.jackson.core.JsonParser, com.fasterxml.jackson.core.JsonToken, java.lang.String, java.lang.Object[]) throws com.fasterxml.jackson.databind.JsonMappingException", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setLocalTime(com.datastax.oss.driver.api.core.CqlIdentifier, java.time.LocalTime) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setLocalTime(com.datastax.oss.driver.api.core.CqlIdentifier, java.time.LocalTime) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method com.fasterxml.jackson.databind.JsonMappingException com.fasterxml.jackson.databind.DeserializationContext::unknownTypeException(com.fasterxml.jackson.databind.JavaType, java.lang.String, java.lang.String)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLocalTime(int, java.time.LocalTime) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLocalTime(int, java.time.LocalTime) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method com.fasterxml.jackson.databind.JsonMappingException com.fasterxml.jackson.databind.DeserializationContext::wrongTokenException(com.fasterxml.jackson.core.JsonParser, com.fasterxml.jackson.core.JsonToken, java.lang.String)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setLocalTime(java.lang.String, java.time.LocalTime) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setLocalTime(java.lang.String, java.time.LocalTime) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.databind.DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT", + "new": "field com.fasterxml.jackson.databind.DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setLong(com.datastax.oss.driver.api.core.CqlIdentifier, long) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setLong(com.datastax.oss.driver.api.core.CqlIdentifier, long) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.databind.DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT", + "new": "field com.fasterxml.jackson.databind.DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLong(int, long) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLong(int, long) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.databind.DeserializationFeature.ACCEPT_FLOAT_AS_INT", + "new": "field com.fasterxml.jackson.databind.DeserializationFeature.ACCEPT_FLOAT_AS_INT", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setLong(java.lang.String, long) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setLong(java.lang.String, long) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.databind.DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY", + "new": "field com.fasterxml.jackson.databind.DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setMap(com.datastax.oss.driver.api.core.CqlIdentifier, java.util.Map, java.lang.Class, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setMap(com.datastax.oss.driver.api.core.CqlIdentifier, java.util.Map, java.lang.Class, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.databind.DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE", + "new": "field com.fasterxml.jackson.databind.DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setMap(int, java.util.Map, java.lang.Class, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setMap(int, java.util.Map, java.lang.Class, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.databind.DeserializationFeature.EAGER_DESERIALIZER_FETCH", + "new": "field com.fasterxml.jackson.databind.DeserializationFeature.EAGER_DESERIALIZER_FETCH", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setMap(java.lang.String, java.util.Map, java.lang.Class, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setMap(java.lang.String, java.util.Map, java.lang.Class, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.databind.DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS", + "new": "field com.fasterxml.jackson.databind.DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setNode(com.datastax.oss.driver.api.core.metadata.Node) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setNode(com.datastax.oss.driver.api.core.metadata.Node) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.databind.DeserializationFeature.READ_ENUMS_USING_TO_STRING", + "new": "field com.fasterxml.jackson.databind.DeserializationFeature.READ_ENUMS_USING_TO_STRING", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setPageSize(int) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setPageSize(int) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.databind.DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL", + "new": "field com.fasterxml.jackson.databind.DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setPagingState(java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setPagingState(java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.databind.DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE", + "new": "field com.fasterxml.jackson.databind.DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingKey(java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingKey(java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.databind.DeserializationFeature.UNWRAP_ROOT_VALUE", + "new": "field com.fasterxml.jackson.databind.DeserializationFeature.UNWRAP_ROOT_VALUE", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingKey(java.nio.ByteBuffer[]) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingKey(java.nio.ByteBuffer[]) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.databind.DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS", + "new": "field com.fasterxml.jackson.databind.DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingKeyspace(com.datastax.oss.driver.api.core.CqlIdentifier) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingKeyspace(com.datastax.oss.driver.api.core.CqlIdentifier) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.databind.DeserializationFeature.WRAP_EXCEPTIONS", + "new": "field com.fasterxml.jackson.databind.DeserializationFeature.WRAP_EXCEPTIONS", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingKeyspace(java.lang.String) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingKeyspace(java.lang.String) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.finalMethodAddedToNonFinalClass", + "new": "method com.fasterxml.jackson.databind.node.ArrayNode com.fasterxml.jackson.databind.JsonNode::withArray(com.fasterxml.jackson.core.JsonPointer)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingToken(com.datastax.oss.driver.api.core.metadata.token.Token) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingToken(com.datastax.oss.driver.api.core.metadata.token.Token) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.finalMethodAddedToNonFinalClass", + "new": "method com.fasterxml.jackson.databind.node.ObjectNode com.fasterxml.jackson.databind.JsonNode::withObject(com.fasterxml.jackson.core.JsonPointer)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setSerialConsistencyLevel(com.datastax.oss.driver.api.core.ConsistencyLevel) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setSerialConsistencyLevel(com.datastax.oss.driver.api.core.ConsistencyLevel) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.finalMethodAddedToNonFinalClass", + "new": "method com.fasterxml.jackson.databind.node.ObjectNode com.fasterxml.jackson.databind.JsonNode::withObject(java.lang.String, com.fasterxml.jackson.databind.JsonNode.OverwriteMode, boolean)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setSet(com.datastax.oss.driver.api.core.CqlIdentifier, java.util.Set, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setSet(com.datastax.oss.driver.api.core.CqlIdentifier, java.util.Set, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.databind.MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS", + "new": "field com.fasterxml.jackson.databind.MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setSet(int, java.util.Set, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setSet(int, java.util.Set, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.databind.MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES", + "new": "field com.fasterxml.jackson.databind.MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setSet(java.lang.String, java.util.Set, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setSet(java.lang.String, java.util.Set, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.databind.MapperFeature.ACCEPT_CASE_INSENSITIVE_VALUES", + "new": "field com.fasterxml.jackson.databind.MapperFeature.ACCEPT_CASE_INSENSITIVE_VALUES", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setShort(com.datastax.oss.driver.api.core.CqlIdentifier, short) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setShort(com.datastax.oss.driver.api.core.CqlIdentifier, short) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.databind.MapperFeature.ALLOW_COERCION_OF_SCALARS", + "new": "field com.fasterxml.jackson.databind.MapperFeature.ALLOW_COERCION_OF_SCALARS", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setShort(int, short) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setShort(int, short) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.databind.MapperFeature.ALLOW_EXPLICIT_PROPERTY_RENAMING", + "new": "field com.fasterxml.jackson.databind.MapperFeature.ALLOW_EXPLICIT_PROPERTY_RENAMING", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setShort(java.lang.String, short) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setShort(java.lang.String, short) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.databind.MapperFeature.ALLOW_VOID_VALUED_PROPERTIES", + "new": "field com.fasterxml.jackson.databind.MapperFeature.ALLOW_VOID_VALUED_PROPERTIES", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setString(com.datastax.oss.driver.api.core.CqlIdentifier, java.lang.String) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setString(com.datastax.oss.driver.api.core.CqlIdentifier, java.lang.String) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.databind.MapperFeature.APPLY_DEFAULT_VALUES", + "new": "field com.fasterxml.jackson.databind.MapperFeature.APPLY_DEFAULT_VALUES", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setString(int, java.lang.String) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setString(int, java.lang.String) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.databind.MapperFeature.BLOCK_UNSAFE_POLYMORPHIC_BASE_TYPES", + "new": "field com.fasterxml.jackson.databind.MapperFeature.BLOCK_UNSAFE_POLYMORPHIC_BASE_TYPES", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setString(java.lang.String, java.lang.String) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setString(java.lang.String, java.lang.String) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.databind.MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS", + "new": "field com.fasterxml.jackson.databind.MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setTimeout(java.time.Duration) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setTimeout(java.time.Duration) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.databind.MapperFeature.DEFAULT_VIEW_INCLUSION", + "new": "field com.fasterxml.jackson.databind.MapperFeature.DEFAULT_VIEW_INCLUSION", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setToNull(com.datastax.oss.driver.api.core.CqlIdentifier) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setToNull(com.datastax.oss.driver.api.core.CqlIdentifier) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.databind.MapperFeature.IGNORE_DUPLICATE_MODULE_REGISTRATIONS", + "new": "field com.fasterxml.jackson.databind.MapperFeature.IGNORE_DUPLICATE_MODULE_REGISTRATIONS", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setToNull(int) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setToNull(int) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.databind.MapperFeature.IGNORE_MERGE_FOR_UNMERGEABLE", + "new": "field com.fasterxml.jackson.databind.MapperFeature.IGNORE_MERGE_FOR_UNMERGEABLE", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setToNull(java.lang.String) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setToNull(java.lang.String) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.databind.MapperFeature.INFER_BUILDER_TYPE_BINDINGS", + "new": "field com.fasterxml.jackson.databind.MapperFeature.INFER_BUILDER_TYPE_BINDINGS", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setToken(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.metadata.token.Token) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setToken(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.metadata.token.Token) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.databind.MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS", + "new": "field com.fasterxml.jackson.databind.MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setToken(int, com.datastax.oss.driver.api.core.metadata.token.Token) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setToken(int, com.datastax.oss.driver.api.core.metadata.token.Token) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.databind.MapperFeature.SORT_CREATOR_PROPERTIES_FIRST", + "new": "field com.fasterxml.jackson.databind.MapperFeature.SORT_CREATOR_PROPERTIES_FIRST", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setToken(java.lang.String, com.datastax.oss.driver.api.core.metadata.token.Token) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setToken(java.lang.String, com.datastax.oss.driver.api.core.metadata.token.Token) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.databind.MapperFeature.SORT_PROPERTIES_ALPHABETICALLY", + "new": "field com.fasterxml.jackson.databind.MapperFeature.SORT_PROPERTIES_ALPHABETICALLY", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setTracing(boolean) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setTracing(boolean) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.databind.MapperFeature.USE_BASE_TYPE_AS_DEFAULT_IMPL", + "new": "field com.fasterxml.jackson.databind.MapperFeature.USE_BASE_TYPE_AS_DEFAULT_IMPL", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setTupleValue(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.data.TupleValue) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setTupleValue(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.data.TupleValue) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.databind.MapperFeature.USE_STATIC_TYPING", + "new": "field com.fasterxml.jackson.databind.MapperFeature.USE_STATIC_TYPING", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setTupleValue(int, com.datastax.oss.driver.api.core.data.TupleValue) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setTupleValue(int, com.datastax.oss.driver.api.core.data.TupleValue) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.databind.MapperFeature.USE_STD_BEAN_NAMING", + "new": "field com.fasterxml.jackson.databind.MapperFeature.USE_STD_BEAN_NAMING", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setTupleValue(java.lang.String, com.datastax.oss.driver.api.core.data.TupleValue) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setTupleValue(java.lang.String, com.datastax.oss.driver.api.core.data.TupleValue) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.databind.MapperFeature.USE_WRAPPER_NAME_AS_PROPERTY_NAME", + "new": "field com.fasterxml.jackson.databind.MapperFeature.USE_WRAPPER_NAME_AS_PROPERTY_NAME", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setUdtValue(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.data.UdtValue) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setUdtValue(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.data.UdtValue) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping.EVERYTHING", + "new": "field com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping.EVERYTHING", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setUdtValue(int, com.datastax.oss.driver.api.core.data.UdtValue) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setUdtValue(int, com.datastax.oss.driver.api.core.data.UdtValue) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.removed", + "old": "field com.fasterxml.jackson.databind.PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setUdtValue(java.lang.String, com.datastax.oss.driver.api.core.data.UdtValue) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setUdtValue(java.lang.String, com.datastax.oss.driver.api.core.data.UdtValue) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.removed", + "old": "field com.fasterxml.jackson.databind.PropertyNamingStrategy.KEBAB_CASE", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setUuid(com.datastax.oss.driver.api.core.CqlIdentifier, java.util.UUID) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setUuid(com.datastax.oss.driver.api.core.CqlIdentifier, java.util.UUID) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.removed", + "old": "field com.fasterxml.jackson.databind.PropertyNamingStrategy.LOWER_CAMEL_CASE", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setUuid(int, java.util.UUID) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setUuid(int, java.util.UUID) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.removed", + "old": "field com.fasterxml.jackson.databind.PropertyNamingStrategy.LOWER_CASE", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setUuid(java.lang.String, java.util.UUID) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setUuid(java.lang.String, java.util.UUID) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.removed", + "old": "field com.fasterxml.jackson.databind.PropertyNamingStrategy.LOWER_DOT_CASE", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Bindable>>::unset(com.datastax.oss.driver.api.core.CqlIdentifier) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Bindable>>::unset(com.datastax.oss.driver.api.core.CqlIdentifier) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.removed", + "old": "field com.fasterxml.jackson.databind.PropertyNamingStrategy.PASCAL_CASE_TO_CAMEL_CASE", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Bindable>>::unset(int) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Bindable>>::unset(int) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.removed", + "old": "field com.fasterxml.jackson.databind.PropertyNamingStrategy.SNAKE_CASE", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Bindable>>::unset(java.lang.String) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Bindable>>::unset(java.lang.String) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.removed", + "old": "field com.fasterxml.jackson.databind.PropertyNamingStrategy.UPPER_CAMEL_CASE", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::set(com.datastax.oss.driver.api.core.CqlIdentifier, ValueT, com.datastax.oss.driver.api.core.type.codec.TypeCodec) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::set(com.datastax.oss.driver.api.core.CqlIdentifier, ValueT, com.datastax.oss.driver.api.core.type.codec.TypeCodec) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.serialVersionUIDUnchanged", + "old": "field com.fasterxml.jackson.databind.SerializationConfig.serialVersionUID", + "new": "field com.fasterxml.jackson.databind.SerializationConfig.serialVersionUID", + "serialVersionUID": "1", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::set(com.datastax.oss.driver.api.core.CqlIdentifier, ValueT, com.datastax.oss.driver.api.core.type.reflect.GenericType) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::set(com.datastax.oss.driver.api.core.CqlIdentifier, ValueT, com.datastax.oss.driver.api.core.type.reflect.GenericType) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.numberOfParametersChanged", + "old": "method void com.fasterxml.jackson.databind.SerializationConfig::(com.fasterxml.jackson.databind.SerializationConfig, com.fasterxml.jackson.databind.introspect.SimpleMixInResolver, com.fasterxml.jackson.databind.util.RootNameLookup, com.fasterxml.jackson.databind.cfg.ConfigOverrides)", + "new": "method void com.fasterxml.jackson.databind.SerializationConfig::(com.fasterxml.jackson.databind.SerializationConfig, com.fasterxml.jackson.databind.cfg.ConstructorDetector)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::set(com.datastax.oss.driver.api.core.CqlIdentifier, ValueT, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::set(com.datastax.oss.driver.api.core.CqlIdentifier, ValueT, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.databind.SerializationFeature.EAGER_SERIALIZER_FETCH", + "new": "field com.fasterxml.jackson.databind.SerializationFeature.EAGER_SERIALIZER_FETCH", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, com.datastax.oss.driver.api.core.type.codec.TypeCodec) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, com.datastax.oss.driver.api.core.type.codec.TypeCodec) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.enumConstantOrderChanged", + "old": "field com.fasterxml.jackson.databind.SerializationFeature.USE_EQUALITY_FOR_OBJECT_ID", + "new": "field com.fasterxml.jackson.databind.SerializationFeature.USE_EQUALITY_FOR_OBJECT_ID", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, com.datastax.oss.driver.api.core.type.reflect.GenericType) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, com.datastax.oss.driver.api.core.type.reflect.GenericType) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.finalMethodAddedToNonFinalClass", + "new": "method com.fasterxml.jackson.databind.cfg.DatatypeFeatures com.fasterxml.jackson.databind.SerializerProvider::getDatatypeFeatures()", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.finalMethodAddedToNonFinalClass", + "new": "method boolean com.fasterxml.jackson.databind.SerializerProvider::isEnabled(com.fasterxml.jackson.databind.cfg.DatatypeFeature)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::set(java.lang.String, ValueT, com.datastax.oss.driver.api.core.type.codec.TypeCodec) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::set(java.lang.String, ValueT, com.datastax.oss.driver.api.core.type.codec.TypeCodec) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.serialVersionUIDUnchanged", + "old": "field com.fasterxml.jackson.databind.cfg.BaseSettings.serialVersionUID", + "new": "field com.fasterxml.jackson.databind.cfg.BaseSettings.serialVersionUID", + "serialVersionUID": "1", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::set(java.lang.String, ValueT, com.datastax.oss.driver.api.core.type.reflect.GenericType) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::set(java.lang.String, ValueT, com.datastax.oss.driver.api.core.type.reflect.GenericType) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.numberOfParametersChanged", + "old": "method void com.fasterxml.jackson.databind.cfg.BaseSettings::(com.fasterxml.jackson.databind.introspect.ClassIntrospector, com.fasterxml.jackson.databind.AnnotationIntrospector, com.fasterxml.jackson.databind.PropertyNamingStrategy, com.fasterxml.jackson.databind.type.TypeFactory, com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder, java.text.DateFormat, com.fasterxml.jackson.databind.cfg.HandlerInstantiator, java.util.Locale, java.util.TimeZone, com.fasterxml.jackson.core.Base64Variant, com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator)", + "new": "method void com.fasterxml.jackson.databind.cfg.BaseSettings::(com.fasterxml.jackson.databind.introspect.ClassIntrospector, com.fasterxml.jackson.databind.AnnotationIntrospector, com.fasterxml.jackson.databind.PropertyNamingStrategy, com.fasterxml.jackson.databind.type.TypeFactory, com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder, java.text.DateFormat, com.fasterxml.jackson.databind.cfg.HandlerInstantiator, java.util.Locale, java.util.TimeZone, com.fasterxml.jackson.core.Base64Variant, com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator, com.fasterxml.jackson.databind.introspect.AccessorNamingStrategy.Provider, com.fasterxml.jackson.databind.cfg.CacheProvider)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::set(java.lang.String, ValueT, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::set(java.lang.String, ValueT, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.abstractMethodAdded", + "new": "method com.fasterxml.jackson.databind.cfg.ConstructorDetector com.fasterxml.jackson.databind.cfg.MapperConfig>::getConstructorDetector()", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setBigDecimal(com.datastax.oss.driver.api.core.CqlIdentifier, java.math.BigDecimal) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setBigDecimal(com.datastax.oss.driver.api.core.CqlIdentifier, java.math.BigDecimal) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.abstractMethodAdded", + "new": "method com.fasterxml.jackson.databind.cfg.DatatypeFeatures com.fasterxml.jackson.databind.cfg.MapperConfig>::getDatatypeFeatures()", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBigDecimal(int, java.math.BigDecimal) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBigDecimal(int, java.math.BigDecimal) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.finalMethodAddedToNonFinalClass", + "new": "method com.fasterxml.jackson.databind.EnumNamingStrategy com.fasterxml.jackson.databind.cfg.MapperConfig>::getEnumNamingStrategy()", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setBigDecimal(java.lang.String, java.math.BigDecimal) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setBigDecimal(java.lang.String, java.math.BigDecimal) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.abstractMethodAdded", + "new": "method boolean com.fasterxml.jackson.databind.cfg.MapperConfig>::isEnabled(com.fasterxml.jackson.databind.cfg.DatatypeFeature)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setBigInteger(com.datastax.oss.driver.api.core.CqlIdentifier, java.math.BigInteger) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setBigInteger(com.datastax.oss.driver.api.core.CqlIdentifier, java.math.BigInteger) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.abstractMethodAdded", + "new": "method com.fasterxml.jackson.databind.deser.DefaultDeserializationContext com.fasterxml.jackson.databind.deser.DefaultDeserializationContext::withCaches(com.fasterxml.jackson.databind.cfg.CacheProvider)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBigInteger(int, java.math.BigInteger) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBigInteger(int, java.math.BigInteger) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.typeChanged", + "old": "field com.fasterxml.jackson.databind.deser.DeserializerCache._cachedDeserializers", + "new": "field com.fasterxml.jackson.databind.deser.DeserializerCache._cachedDeserializers", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setBigInteger(java.lang.String, java.math.BigInteger) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setBigInteger(java.lang.String, java.math.BigInteger) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.serialVersionUIDUnchanged", + "old": "field com.fasterxml.jackson.databind.deser.DeserializerCache.serialVersionUID", + "new": "field com.fasterxml.jackson.databind.deser.DeserializerCache.serialVersionUID", + "serialVersionUID": "1", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setBoolean(com.datastax.oss.driver.api.core.CqlIdentifier, boolean) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setBoolean(com.datastax.oss.driver.api.core.CqlIdentifier, boolean) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method void com.fasterxml.jackson.databind.deser.SettableAnyProperty::(com.fasterxml.jackson.databind.BeanProperty, com.fasterxml.jackson.databind.introspect.AnnotatedMember, com.fasterxml.jackson.databind.JavaType, com.fasterxml.jackson.databind.JsonDeserializer, com.fasterxml.jackson.databind.jsontype.TypeDeserializer)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBoolean(int, boolean) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBoolean(int, boolean) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.abstractMethodAdded", + "new": "method void com.fasterxml.jackson.databind.deser.SettableAnyProperty::_set(java.lang.Object, java.lang.Object, java.lang.Object) throws java.lang.Exception", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setBoolean(java.lang.String, boolean) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setBoolean(java.lang.String, boolean) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.nowAbstract", + "old": "method com.fasterxml.jackson.databind.deser.SettableAnyProperty com.fasterxml.jackson.databind.deser.SettableAnyProperty::withValueDeserializer(com.fasterxml.jackson.databind.JsonDeserializer)", + "new": "method com.fasterxml.jackson.databind.deser.SettableAnyProperty com.fasterxml.jackson.databind.deser.SettableAnyProperty::withValueDeserializer(com.fasterxml.jackson.databind.JsonDeserializer)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setByte(com.datastax.oss.driver.api.core.CqlIdentifier, byte) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setByte(com.datastax.oss.driver.api.core.CqlIdentifier, byte) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.class.nowAbstract", + "old": "class com.fasterxml.jackson.databind.deser.SettableAnyProperty", + "new": "class com.fasterxml.jackson.databind.deser.SettableAnyProperty", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setByte(int, byte) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setByte(int, byte) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.class.defaultSerializationChanged", + "old": "class com.fasterxml.jackson.databind.deser.SettableBeanProperty", + "new": "class com.fasterxml.jackson.databind.deser.SettableBeanProperty", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setByte(java.lang.String, byte) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setByte(java.lang.String, byte) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method void com.fasterxml.jackson.databind.deser.UnresolvedForwardReference::(java.lang.String)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setByteBuffer(com.datastax.oss.driver.api.core.CqlIdentifier, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setByteBuffer(com.datastax.oss.driver.api.core.CqlIdentifier, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method void com.fasterxml.jackson.databind.deser.UnresolvedForwardReference::(java.lang.String, com.fasterxml.jackson.core.JsonLocation, com.fasterxml.jackson.databind.deser.impl.ReadableObjectId)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setByteBuffer(int, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setByteBuffer(int, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.nowFinal", + "old": "method void com.fasterxml.jackson.databind.deser.impl.PropertyValue::assign(java.lang.Object) throws java.io.IOException", + "new": "method void com.fasterxml.jackson.databind.deser.impl.PropertyValue::assign(java.lang.Object) throws java.io.IOException", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setByteBuffer(java.lang.String, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setByteBuffer(java.lang.String, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.exception.checkedAdded", + "old": "method java.lang.Object[] com.fasterxml.jackson.databind.deser.impl.PropertyValueBuffer::getParameters(com.fasterxml.jackson.databind.deser.SettableBeanProperty[]) throws com.fasterxml.jackson.databind.JsonMappingException", + "new": "method java.lang.Object[] com.fasterxml.jackson.databind.deser.impl.PropertyValueBuffer::getParameters(com.fasterxml.jackson.databind.deser.SettableBeanProperty[]) throws com.fasterxml.jackson.databind.JsonMappingException, java.io.IOException", + "exception": "java.io.IOException", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setBytesUnsafe(com.datastax.oss.driver.api.core.CqlIdentifier, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setBytesUnsafe(com.datastax.oss.driver.api.core.CqlIdentifier, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.numberOfParametersChanged", + "old": "method void com.fasterxml.jackson.databind.deser.impl.ValueInjector::(com.fasterxml.jackson.databind.PropertyName, com.fasterxml.jackson.databind.JavaType, com.fasterxml.jackson.databind.util.Annotations, com.fasterxml.jackson.databind.introspect.AnnotatedMember, java.lang.Object)", + "new": "method void com.fasterxml.jackson.databind.deser.impl.ValueInjector::(com.fasterxml.jackson.databind.PropertyName, com.fasterxml.jackson.databind.JavaType, com.fasterxml.jackson.databind.introspect.AnnotatedMember, java.lang.Object, java.lang.Boolean, java.lang.Boolean)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setBytesUnsafe(java.lang.String, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setBytesUnsafe(java.lang.String, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method com.fasterxml.jackson.databind.introspect.AnnotatedClass com.fasterxml.jackson.databind.introspect.AnnotatedClass::construct(com.fasterxml.jackson.databind.JavaType, com.fasterxml.jackson.databind.cfg.MapperConfig)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setCqlDuration(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.data.CqlDuration) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setCqlDuration(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.data.CqlDuration) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method com.fasterxml.jackson.databind.introspect.AnnotatedClass com.fasterxml.jackson.databind.introspect.AnnotatedClass::construct(com.fasterxml.jackson.databind.JavaType, com.fasterxml.jackson.databind.cfg.MapperConfig, com.fasterxml.jackson.databind.introspect.ClassIntrospector.MixInResolver)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setCqlDuration(int, com.datastax.oss.driver.api.core.data.CqlDuration) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setCqlDuration(int, com.datastax.oss.driver.api.core.data.CqlDuration) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method com.fasterxml.jackson.databind.introspect.AnnotatedClass com.fasterxml.jackson.databind.introspect.AnnotatedClass::constructWithoutSuperTypes(java.lang.Class, com.fasterxml.jackson.databind.cfg.MapperConfig)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setCqlDuration(java.lang.String, com.datastax.oss.driver.api.core.data.CqlDuration) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setCqlDuration(java.lang.String, com.datastax.oss.driver.api.core.data.CqlDuration) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method com.fasterxml.jackson.databind.introspect.AnnotatedClass com.fasterxml.jackson.databind.introspect.AnnotatedClass::constructWithoutSuperTypes(java.lang.Class, com.fasterxml.jackson.databind.cfg.MapperConfig, com.fasterxml.jackson.databind.introspect.ClassIntrospector.MixInResolver)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setDouble(com.datastax.oss.driver.api.core.CqlIdentifier, double) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setDouble(com.datastax.oss.driver.api.core.CqlIdentifier, double) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method java.util.List com.fasterxml.jackson.databind.introspect.AnnotatedClass::getStaticMethods()", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setDouble(int, double) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setDouble(int, double) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method com.fasterxml.jackson.databind.BeanDescription com.fasterxml.jackson.databind.introspect.ClassIntrospector::forDeserializationWithBuilder(com.fasterxml.jackson.databind.DeserializationConfig, com.fasterxml.jackson.databind.JavaType, com.fasterxml.jackson.databind.introspect.ClassIntrospector.MixInResolver)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setDouble(java.lang.String, double) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setDouble(java.lang.String, double) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.returnTypeChangedCovariantly", + "old": "method T com.fasterxml.jackson.databind.JsonNode::with(java.lang.String) @ com.fasterxml.jackson.databind.node.ArrayNode", + "new": "method com.fasterxml.jackson.databind.node.ObjectNode com.fasterxml.jackson.databind.node.ArrayNode::with(java.lang.String)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setFloat(com.datastax.oss.driver.api.core.CqlIdentifier, float) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setFloat(com.datastax.oss.driver.api.core.CqlIdentifier, float) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.generics.formalTypeParameterRemoved", + "old": "method T com.fasterxml.jackson.databind.JsonNode::with(java.lang.String) @ com.fasterxml.jackson.databind.node.ArrayNode", + "new": "method com.fasterxml.jackson.databind.node.ObjectNode com.fasterxml.jackson.databind.node.ArrayNode::with(java.lang.String)", + "typeParameter": "T extends com.fasterxml.jackson.databind.JsonNode", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setFloat(int, float) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setFloat(int, float) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.finalMethodAddedToNonFinalClass", + "new": "method com.fasterxml.jackson.annotation.JsonFormat.Value com.fasterxml.jackson.databind.introspect.ConcreteBeanPropertyBase::findFormatOverrides(com.fasterxml.jackson.databind.AnnotationIntrospector) @ com.fasterxml.jackson.databind.ser.AnyGetterWriter", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setFloat(java.lang.String, float) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setFloat(java.lang.String, float) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.class.nonFinalClassInheritsFromNewClass", + "old": "class com.fasterxml.jackson.databind.ser.AnyGetterWriter", + "new": "class com.fasterxml.jackson.databind.ser.AnyGetterWriter", + "superClass": "com.fasterxml.jackson.databind.introspect.ConcreteBeanPropertyBase", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setInetAddress(com.datastax.oss.driver.api.core.CqlIdentifier, java.net.InetAddress) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setInetAddress(com.datastax.oss.driver.api.core.CqlIdentifier, java.net.InetAddress) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.abstractMethodAdded", + "new": "method com.fasterxml.jackson.databind.ser.DefaultSerializerProvider com.fasterxml.jackson.databind.ser.DefaultSerializerProvider::withCaches(com.fasterxml.jackson.databind.cfg.CacheProvider)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInetAddress(int, java.net.InetAddress) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInetAddress(int, java.net.InetAddress) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.parameterTypeChanged", + "old": "parameter void com.fasterxml.jackson.databind.ser.impl.ReadOnlyClassToSerializerMap::(===java.util.Map>===)", + "new": "parameter void com.fasterxml.jackson.databind.ser.impl.ReadOnlyClassToSerializerMap::(===com.fasterxml.jackson.databind.util.LookupCache>===)", + "parameterIndex": "0", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setInetAddress(java.lang.String, java.net.InetAddress) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setInetAddress(java.lang.String, java.net.InetAddress) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.parameterTypeChanged", + "old": "parameter com.fasterxml.jackson.databind.ser.impl.ReadOnlyClassToSerializerMap com.fasterxml.jackson.databind.ser.impl.ReadOnlyClassToSerializerMap::from(===java.util.HashMap>===)", + "new": "parameter com.fasterxml.jackson.databind.ser.impl.ReadOnlyClassToSerializerMap com.fasterxml.jackson.databind.ser.impl.ReadOnlyClassToSerializerMap::from(===com.fasterxml.jackson.databind.util.LookupCache>===)", + "parameterIndex": "0", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setInstant(com.datastax.oss.driver.api.core.CqlIdentifier, java.time.Instant) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setInstant(com.datastax.oss.driver.api.core.CqlIdentifier, java.time.Instant) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.removed", + "old": "field com.fasterxml.jackson.databind.ser.std.BeanSerializerBase._anyGetterWriter", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInstant(int, java.time.Instant) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInstant(int, java.time.Instant) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.class.nonFinalClassInheritsFromNewClass", + "old": "class com.fasterxml.jackson.databind.type.ResolvedRecursiveType", + "new": "class com.fasterxml.jackson.databind.type.ResolvedRecursiveType", + "superClass": "com.fasterxml.jackson.databind.type.IdentityEqualityType", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setInstant(java.lang.String, java.time.Instant) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setInstant(java.lang.String, java.time.Instant) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.field.removed", + "old": "field com.fasterxml.jackson.databind.type.TypeFactory.CORE_TYPE_CLASS", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setInt(com.datastax.oss.driver.api.core.CqlIdentifier, int) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setInt(com.datastax.oss.driver.api.core.CqlIdentifier, int) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method void com.fasterxml.jackson.databind.type.TypeFactory::(com.fasterxml.jackson.databind.util.LRUMap, com.fasterxml.jackson.databind.type.TypeParser, com.fasterxml.jackson.databind.type.TypeModifier[], java.lang.ClassLoader)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInt(int, int) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInt(int, int) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.removed", + "old": "method void com.fasterxml.jackson.databind.type.TypeFactory::(com.fasterxml.jackson.databind.util.LRUMap)", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setInt(java.lang.String, int) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setInt(java.lang.String, int) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" + "ignore": true, + "code": "java.method.numberOfParametersChanged", + "old": "method com.fasterxml.jackson.databind.JavaType com.fasterxml.jackson.databind.type.TypeParser::parseType(com.fasterxml.jackson.databind.type.TypeParser.MyTokenizer) throws java.lang.IllegalArgumentException", + "new": "method com.fasterxml.jackson.databind.JavaType com.fasterxml.jackson.databind.type.TypeParser::parseType(com.fasterxml.jackson.databind.type.TypeParser.MyTokenizer, int) throws java.lang.IllegalArgumentException", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setList(com.datastax.oss.driver.api.core.CqlIdentifier, java.util.List, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setList(com.datastax.oss.driver.api.core.CqlIdentifier, java.util.List, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setList(int, java.util.List, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setList(int, java.util.List, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setList(java.lang.String, java.util.List, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setList(java.lang.String, java.util.List, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setLocalDate(com.datastax.oss.driver.api.core.CqlIdentifier, java.time.LocalDate) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setLocalDate(com.datastax.oss.driver.api.core.CqlIdentifier, java.time.LocalDate) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLocalDate(int, java.time.LocalDate) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLocalDate(int, java.time.LocalDate) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setLocalDate(java.lang.String, java.time.LocalDate) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setLocalDate(java.lang.String, java.time.LocalDate) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setLocalTime(com.datastax.oss.driver.api.core.CqlIdentifier, java.time.LocalTime) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setLocalTime(com.datastax.oss.driver.api.core.CqlIdentifier, java.time.LocalTime) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLocalTime(int, java.time.LocalTime) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLocalTime(int, java.time.LocalTime) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setLocalTime(java.lang.String, java.time.LocalTime) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setLocalTime(java.lang.String, java.time.LocalTime) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setLong(com.datastax.oss.driver.api.core.CqlIdentifier, long) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setLong(com.datastax.oss.driver.api.core.CqlIdentifier, long) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLong(int, long) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLong(int, long) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setLong(java.lang.String, long) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setLong(java.lang.String, long) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setMap(com.datastax.oss.driver.api.core.CqlIdentifier, java.util.Map, java.lang.Class, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setMap(com.datastax.oss.driver.api.core.CqlIdentifier, java.util.Map, java.lang.Class, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setMap(int, java.util.Map, java.lang.Class, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setMap(int, java.util.Map, java.lang.Class, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setMap(java.lang.String, java.util.Map, java.lang.Class, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setMap(java.lang.String, java.util.Map, java.lang.Class, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setSet(com.datastax.oss.driver.api.core.CqlIdentifier, java.util.Set, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setSet(com.datastax.oss.driver.api.core.CqlIdentifier, java.util.Set, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setSet(int, java.util.Set, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setSet(int, java.util.Set, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setSet(java.lang.String, java.util.Set, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setSet(java.lang.String, java.util.Set, java.lang.Class) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setShort(com.datastax.oss.driver.api.core.CqlIdentifier, short) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setShort(com.datastax.oss.driver.api.core.CqlIdentifier, short) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setShort(int, short) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setShort(int, short) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setShort(java.lang.String, short) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setShort(java.lang.String, short) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setString(com.datastax.oss.driver.api.core.CqlIdentifier, java.lang.String) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setString(com.datastax.oss.driver.api.core.CqlIdentifier, java.lang.String) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setString(int, java.lang.String) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setString(int, java.lang.String) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setString(java.lang.String, java.lang.String) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setString(java.lang.String, java.lang.String) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setToNull(com.datastax.oss.driver.api.core.CqlIdentifier) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setToNull(com.datastax.oss.driver.api.core.CqlIdentifier) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setToNull(int) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setToNull(int) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setToNull(java.lang.String) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setToNull(java.lang.String) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setToken(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.metadata.token.Token) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setToken(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.metadata.token.Token) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setToken(int, com.datastax.oss.driver.api.core.metadata.token.Token) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setToken(int, com.datastax.oss.driver.api.core.metadata.token.Token) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setToken(java.lang.String, com.datastax.oss.driver.api.core.metadata.token.Token) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setToken(java.lang.String, com.datastax.oss.driver.api.core.metadata.token.Token) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setTupleValue(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.data.TupleValue) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setTupleValue(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.data.TupleValue) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setTupleValue(int, com.datastax.oss.driver.api.core.data.TupleValue) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setTupleValue(int, com.datastax.oss.driver.api.core.data.TupleValue) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setTupleValue(java.lang.String, com.datastax.oss.driver.api.core.data.TupleValue) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setTupleValue(java.lang.String, com.datastax.oss.driver.api.core.data.TupleValue) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setUdtValue(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.data.UdtValue) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setUdtValue(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.data.UdtValue) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setUdtValue(int, com.datastax.oss.driver.api.core.data.UdtValue) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setUdtValue(int, com.datastax.oss.driver.api.core.data.UdtValue) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setUdtValue(java.lang.String, com.datastax.oss.driver.api.core.data.UdtValue) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setUdtValue(java.lang.String, com.datastax.oss.driver.api.core.data.UdtValue) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setUuid(com.datastax.oss.driver.api.core.CqlIdentifier, java.util.UUID) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setUuid(com.datastax.oss.driver.api.core.CqlIdentifier, java.util.UUID) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setUuid(int, java.util.UUID) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setUuid(int, java.util.UUID) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setUuid(java.lang.String, java.util.UUID) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setUuid(java.lang.String, java.util.UUID) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Bindable>>::unset(com.datastax.oss.driver.api.core.CqlIdentifier) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Bindable>>::unset(com.datastax.oss.driver.api.core.CqlIdentifier) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Bindable>>::unset(int) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Bindable>>::unset(int) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Bindable>>::unset(java.lang.String) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Bindable>>::unset(java.lang.String) @ com.datastax.oss.driver.api.core.cql.BoundStatementBuilder", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::copy(java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.SimpleStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::copy(java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.SimpleStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setConsistencyLevel(com.datastax.oss.driver.api.core.ConsistencyLevel) @ com.datastax.oss.driver.api.core.cql.SimpleStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setConsistencyLevel(com.datastax.oss.driver.api.core.ConsistencyLevel) @ com.datastax.oss.driver.api.core.cql.SimpleStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setCustomPayload(java.util.Map) @ com.datastax.oss.driver.api.core.cql.SimpleStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setCustomPayload(java.util.Map) @ com.datastax.oss.driver.api.core.cql.SimpleStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setExecutionProfile(com.datastax.oss.driver.api.core.config.DriverExecutionProfile) @ com.datastax.oss.driver.api.core.cql.SimpleStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setExecutionProfile(com.datastax.oss.driver.api.core.config.DriverExecutionProfile) @ com.datastax.oss.driver.api.core.cql.SimpleStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setExecutionProfileName(java.lang.String) @ com.datastax.oss.driver.api.core.cql.SimpleStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setExecutionProfileName(java.lang.String) @ com.datastax.oss.driver.api.core.cql.SimpleStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setIdempotent(java.lang.Boolean) @ com.datastax.oss.driver.api.core.cql.SimpleStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setIdempotent(java.lang.Boolean) @ com.datastax.oss.driver.api.core.cql.SimpleStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setNode(com.datastax.oss.driver.api.core.metadata.Node) @ com.datastax.oss.driver.api.core.cql.SimpleStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setNode(com.datastax.oss.driver.api.core.metadata.Node) @ com.datastax.oss.driver.api.core.cql.SimpleStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setPageSize(int) @ com.datastax.oss.driver.api.core.cql.SimpleStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setPageSize(int) @ com.datastax.oss.driver.api.core.cql.SimpleStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setPagingState(java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.SimpleStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setPagingState(java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.SimpleStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingKey(java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.SimpleStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingKey(java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.cql.SimpleStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingKey(java.nio.ByteBuffer[]) @ com.datastax.oss.driver.api.core.cql.SimpleStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingKey(java.nio.ByteBuffer[]) @ com.datastax.oss.driver.api.core.cql.SimpleStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingKeyspace(com.datastax.oss.driver.api.core.CqlIdentifier) @ com.datastax.oss.driver.api.core.cql.SimpleStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingKeyspace(com.datastax.oss.driver.api.core.CqlIdentifier) @ com.datastax.oss.driver.api.core.cql.SimpleStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingKeyspace(java.lang.String) @ com.datastax.oss.driver.api.core.cql.SimpleStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingKeyspace(java.lang.String) @ com.datastax.oss.driver.api.core.cql.SimpleStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingToken(com.datastax.oss.driver.api.core.metadata.token.Token) @ com.datastax.oss.driver.api.core.cql.SimpleStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingToken(com.datastax.oss.driver.api.core.metadata.token.Token) @ com.datastax.oss.driver.api.core.cql.SimpleStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setSerialConsistencyLevel(com.datastax.oss.driver.api.core.ConsistencyLevel) @ com.datastax.oss.driver.api.core.cql.SimpleStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setSerialConsistencyLevel(com.datastax.oss.driver.api.core.ConsistencyLevel) @ com.datastax.oss.driver.api.core.cql.SimpleStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setTimeout(java.time.Duration) @ com.datastax.oss.driver.api.core.cql.SimpleStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setTimeout(java.time.Duration) @ com.datastax.oss.driver.api.core.cql.SimpleStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setTracing(boolean) @ com.datastax.oss.driver.api.core.cql.SimpleStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setTracing(boolean) @ com.datastax.oss.driver.api.core.cql.SimpleStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::copy(java.nio.ByteBuffer)", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::copy(java.nio.ByteBuffer)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setConsistencyLevel(com.datastax.oss.driver.api.core.ConsistencyLevel)", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setConsistencyLevel(com.datastax.oss.driver.api.core.ConsistencyLevel)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setCustomPayload(java.util.Map)", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setCustomPayload(java.util.Map)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setExecutionProfile(com.datastax.oss.driver.api.core.config.DriverExecutionProfile)", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setExecutionProfile(com.datastax.oss.driver.api.core.config.DriverExecutionProfile)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setExecutionProfileName(java.lang.String)", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setExecutionProfileName(java.lang.String)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setIdempotent(java.lang.Boolean)", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setIdempotent(java.lang.Boolean)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setNode(com.datastax.oss.driver.api.core.metadata.Node)", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setNode(com.datastax.oss.driver.api.core.metadata.Node)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setPageSize(int)", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setPageSize(int)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setPagingState(java.nio.ByteBuffer)", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setPagingState(java.nio.ByteBuffer)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingKey(java.nio.ByteBuffer)", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingKey(java.nio.ByteBuffer)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingKey(java.nio.ByteBuffer[])", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingKey(java.nio.ByteBuffer[])", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingKeyspace(com.datastax.oss.driver.api.core.CqlIdentifier)", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingKeyspace(com.datastax.oss.driver.api.core.CqlIdentifier)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingKeyspace(java.lang.String)", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingKeyspace(java.lang.String)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingToken(com.datastax.oss.driver.api.core.metadata.token.Token)", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setRoutingToken(com.datastax.oss.driver.api.core.metadata.token.Token)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setSerialConsistencyLevel(com.datastax.oss.driver.api.core.ConsistencyLevel)", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setSerialConsistencyLevel(com.datastax.oss.driver.api.core.ConsistencyLevel)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setTimeout(java.time.Duration)", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setTimeout(java.time.Duration)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setTracing(boolean)", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setTracing(boolean)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::set(com.datastax.oss.driver.api.core.CqlIdentifier, ValueT, com.datastax.oss.driver.api.core.type.codec.TypeCodec)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::set(com.datastax.oss.driver.api.core.CqlIdentifier, ValueT, com.datastax.oss.driver.api.core.type.codec.TypeCodec)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::set(com.datastax.oss.driver.api.core.CqlIdentifier, ValueT, com.datastax.oss.driver.api.core.type.reflect.GenericType)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::set(com.datastax.oss.driver.api.core.CqlIdentifier, ValueT, com.datastax.oss.driver.api.core.type.reflect.GenericType)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::set(com.datastax.oss.driver.api.core.CqlIdentifier, ValueT, java.lang.Class)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::set(com.datastax.oss.driver.api.core.CqlIdentifier, ValueT, java.lang.Class)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, com.datastax.oss.driver.api.core.type.codec.TypeCodec) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, com.datastax.oss.driver.api.core.type.codec.TypeCodec) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, com.datastax.oss.driver.api.core.type.reflect.GenericType) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, com.datastax.oss.driver.api.core.type.reflect.GenericType) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, java.lang.Class) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, java.lang.Class) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setBigDecimal(com.datastax.oss.driver.api.core.CqlIdentifier, java.math.BigDecimal)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setBigDecimal(com.datastax.oss.driver.api.core.CqlIdentifier, java.math.BigDecimal)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBigDecimal(int, java.math.BigDecimal) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBigDecimal(int, java.math.BigDecimal) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setBigInteger(com.datastax.oss.driver.api.core.CqlIdentifier, java.math.BigInteger)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setBigInteger(com.datastax.oss.driver.api.core.CqlIdentifier, java.math.BigInteger)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBigInteger(int, java.math.BigInteger) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBigInteger(int, java.math.BigInteger) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setBoolean(com.datastax.oss.driver.api.core.CqlIdentifier, boolean)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setBoolean(com.datastax.oss.driver.api.core.CqlIdentifier, boolean)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBoolean(int, boolean) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBoolean(int, boolean) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setByte(com.datastax.oss.driver.api.core.CqlIdentifier, byte)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setByte(com.datastax.oss.driver.api.core.CqlIdentifier, byte)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setByte(int, byte) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setByte(int, byte) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setByteBuffer(com.datastax.oss.driver.api.core.CqlIdentifier, java.nio.ByteBuffer)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setByteBuffer(com.datastax.oss.driver.api.core.CqlIdentifier, java.nio.ByteBuffer)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setByteBuffer(int, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setByteBuffer(int, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setBytesUnsafe(com.datastax.oss.driver.api.core.CqlIdentifier, java.nio.ByteBuffer)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setBytesUnsafe(com.datastax.oss.driver.api.core.CqlIdentifier, java.nio.ByteBuffer)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBytesUnsafe(int, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBytesUnsafe(int, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setCqlDuration(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.data.CqlDuration)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setCqlDuration(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.data.CqlDuration)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setCqlDuration(int, com.datastax.oss.driver.api.core.data.CqlDuration) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setCqlDuration(int, com.datastax.oss.driver.api.core.data.CqlDuration) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setDouble(com.datastax.oss.driver.api.core.CqlIdentifier, double)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setDouble(com.datastax.oss.driver.api.core.CqlIdentifier, double)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setDouble(int, double) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setDouble(int, double) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setFloat(com.datastax.oss.driver.api.core.CqlIdentifier, float)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setFloat(com.datastax.oss.driver.api.core.CqlIdentifier, float)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setFloat(int, float) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setFloat(int, float) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setInetAddress(com.datastax.oss.driver.api.core.CqlIdentifier, java.net.InetAddress)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setInetAddress(com.datastax.oss.driver.api.core.CqlIdentifier, java.net.InetAddress)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInetAddress(int, java.net.InetAddress) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInetAddress(int, java.net.InetAddress) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setInstant(com.datastax.oss.driver.api.core.CqlIdentifier, java.time.Instant)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setInstant(com.datastax.oss.driver.api.core.CqlIdentifier, java.time.Instant)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInstant(int, java.time.Instant) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInstant(int, java.time.Instant) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setInt(com.datastax.oss.driver.api.core.CqlIdentifier, int)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setInt(com.datastax.oss.driver.api.core.CqlIdentifier, int)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInt(int, int) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInt(int, int) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setList(com.datastax.oss.driver.api.core.CqlIdentifier, java.util.List, java.lang.Class)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setList(com.datastax.oss.driver.api.core.CqlIdentifier, java.util.List, java.lang.Class)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setList(int, java.util.List, java.lang.Class) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setList(int, java.util.List, java.lang.Class) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setLocalDate(com.datastax.oss.driver.api.core.CqlIdentifier, java.time.LocalDate)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setLocalDate(com.datastax.oss.driver.api.core.CqlIdentifier, java.time.LocalDate)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLocalDate(int, java.time.LocalDate) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLocalDate(int, java.time.LocalDate) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setLocalTime(com.datastax.oss.driver.api.core.CqlIdentifier, java.time.LocalTime)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setLocalTime(com.datastax.oss.driver.api.core.CqlIdentifier, java.time.LocalTime)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLocalTime(int, java.time.LocalTime) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLocalTime(int, java.time.LocalTime) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setLong(com.datastax.oss.driver.api.core.CqlIdentifier, long)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setLong(com.datastax.oss.driver.api.core.CqlIdentifier, long)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLong(int, long) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLong(int, long) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setMap(com.datastax.oss.driver.api.core.CqlIdentifier, java.util.Map, java.lang.Class, java.lang.Class)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setMap(com.datastax.oss.driver.api.core.CqlIdentifier, java.util.Map, java.lang.Class, java.lang.Class)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setMap(int, java.util.Map, java.lang.Class, java.lang.Class) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setMap(int, java.util.Map, java.lang.Class, java.lang.Class) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setSet(com.datastax.oss.driver.api.core.CqlIdentifier, java.util.Set, java.lang.Class)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setSet(com.datastax.oss.driver.api.core.CqlIdentifier, java.util.Set, java.lang.Class)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setSet(int, java.util.Set, java.lang.Class) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setSet(int, java.util.Set, java.lang.Class) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setShort(com.datastax.oss.driver.api.core.CqlIdentifier, short)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setShort(com.datastax.oss.driver.api.core.CqlIdentifier, short)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setShort(int, short) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setShort(int, short) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setString(com.datastax.oss.driver.api.core.CqlIdentifier, java.lang.String)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setString(com.datastax.oss.driver.api.core.CqlIdentifier, java.lang.String)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setString(int, java.lang.String) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setString(int, java.lang.String) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setToNull(com.datastax.oss.driver.api.core.CqlIdentifier)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setToNull(com.datastax.oss.driver.api.core.CqlIdentifier)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setToNull(int) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setToNull(int) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setToken(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.metadata.token.Token)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setToken(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.metadata.token.Token)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setToken(int, com.datastax.oss.driver.api.core.metadata.token.Token) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setToken(int, com.datastax.oss.driver.api.core.metadata.token.Token) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setTupleValue(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.data.TupleValue)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setTupleValue(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.data.TupleValue)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setTupleValue(int, com.datastax.oss.driver.api.core.data.TupleValue) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setTupleValue(int, com.datastax.oss.driver.api.core.data.TupleValue) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setUdtValue(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.data.UdtValue)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setUdtValue(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.data.UdtValue)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setUdtValue(int, com.datastax.oss.driver.api.core.data.UdtValue) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setUdtValue(int, com.datastax.oss.driver.api.core.data.UdtValue) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setUuid(com.datastax.oss.driver.api.core.CqlIdentifier, java.util.UUID)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setUuid(com.datastax.oss.driver.api.core.CqlIdentifier, java.util.UUID)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setUuid(int, java.util.UUID) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setUuid(int, java.util.UUID) @ com.datastax.oss.driver.api.core.data.SettableById>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, com.datastax.oss.driver.api.core.type.codec.TypeCodec)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, com.datastax.oss.driver.api.core.type.codec.TypeCodec)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, com.datastax.oss.driver.api.core.type.reflect.GenericType)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, com.datastax.oss.driver.api.core.type.reflect.GenericType)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, java.lang.Class)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, java.lang.Class)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBigDecimal(int, java.math.BigDecimal)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBigDecimal(int, java.math.BigDecimal)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBigInteger(int, java.math.BigInteger)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBigInteger(int, java.math.BigInteger)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBoolean(int, boolean)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBoolean(int, boolean)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setByte(int, byte)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setByte(int, byte)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setByteBuffer(int, java.nio.ByteBuffer)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setByteBuffer(int, java.nio.ByteBuffer)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBytesUnsafe(int, java.nio.ByteBuffer)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBytesUnsafe(int, java.nio.ByteBuffer)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setCqlDuration(int, com.datastax.oss.driver.api.core.data.CqlDuration)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setCqlDuration(int, com.datastax.oss.driver.api.core.data.CqlDuration)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setDouble(int, double)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setDouble(int, double)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setFloat(int, float)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setFloat(int, float)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInetAddress(int, java.net.InetAddress)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInetAddress(int, java.net.InetAddress)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInstant(int, java.time.Instant)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInstant(int, java.time.Instant)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInt(int, int)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInt(int, int)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setList(int, java.util.List, java.lang.Class)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setList(int, java.util.List, java.lang.Class)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLocalDate(int, java.time.LocalDate)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLocalDate(int, java.time.LocalDate)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLocalTime(int, java.time.LocalTime)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLocalTime(int, java.time.LocalTime)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLong(int, long)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLong(int, long)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setMap(int, java.util.Map, java.lang.Class, java.lang.Class)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setMap(int, java.util.Map, java.lang.Class, java.lang.Class)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setSet(int, java.util.Set, java.lang.Class)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setSet(int, java.util.Set, java.lang.Class)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setShort(int, short)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setShort(int, short)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setString(int, java.lang.String)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setString(int, java.lang.String)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setToNull(int)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setToNull(int)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setToken(int, com.datastax.oss.driver.api.core.metadata.token.Token)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setToken(int, com.datastax.oss.driver.api.core.metadata.token.Token)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setTupleValue(int, com.datastax.oss.driver.api.core.data.TupleValue)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setTupleValue(int, com.datastax.oss.driver.api.core.data.TupleValue)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setUdtValue(int, com.datastax.oss.driver.api.core.data.UdtValue)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setUdtValue(int, com.datastax.oss.driver.api.core.data.UdtValue)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setUuid(int, java.util.UUID)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setUuid(int, java.util.UUID)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, com.datastax.oss.driver.api.core.type.codec.TypeCodec) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, com.datastax.oss.driver.api.core.type.codec.TypeCodec) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, com.datastax.oss.driver.api.core.type.reflect.GenericType) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, com.datastax.oss.driver.api.core.type.reflect.GenericType) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, java.lang.Class) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, java.lang.Class) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::set(java.lang.String, ValueT, com.datastax.oss.driver.api.core.type.codec.TypeCodec)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::set(java.lang.String, ValueT, com.datastax.oss.driver.api.core.type.codec.TypeCodec)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::set(java.lang.String, ValueT, com.datastax.oss.driver.api.core.type.reflect.GenericType)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::set(java.lang.String, ValueT, com.datastax.oss.driver.api.core.type.reflect.GenericType)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::set(java.lang.String, ValueT, java.lang.Class)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::set(java.lang.String, ValueT, java.lang.Class)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBigDecimal(int, java.math.BigDecimal) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBigDecimal(int, java.math.BigDecimal) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setBigDecimal(java.lang.String, java.math.BigDecimal)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setBigDecimal(java.lang.String, java.math.BigDecimal)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBigInteger(int, java.math.BigInteger) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBigInteger(int, java.math.BigInteger) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setBigInteger(java.lang.String, java.math.BigInteger)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setBigInteger(java.lang.String, java.math.BigInteger)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBoolean(int, boolean) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBoolean(int, boolean) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setBoolean(java.lang.String, boolean)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setBoolean(java.lang.String, boolean)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setByte(int, byte) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setByte(int, byte) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setByte(java.lang.String, byte)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setByte(java.lang.String, byte)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setByteBuffer(int, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setByteBuffer(int, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setByteBuffer(java.lang.String, java.nio.ByteBuffer)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setByteBuffer(java.lang.String, java.nio.ByteBuffer)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBytesUnsafe(int, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBytesUnsafe(int, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setBytesUnsafe(java.lang.String, java.nio.ByteBuffer)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setBytesUnsafe(java.lang.String, java.nio.ByteBuffer)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setCqlDuration(int, com.datastax.oss.driver.api.core.data.CqlDuration) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setCqlDuration(int, com.datastax.oss.driver.api.core.data.CqlDuration) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setCqlDuration(java.lang.String, com.datastax.oss.driver.api.core.data.CqlDuration)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setCqlDuration(java.lang.String, com.datastax.oss.driver.api.core.data.CqlDuration)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setDouble(int, double) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setDouble(int, double) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setDouble(java.lang.String, double)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setDouble(java.lang.String, double)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setFloat(int, float) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setFloat(int, float) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setFloat(java.lang.String, float)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setFloat(java.lang.String, float)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInetAddress(int, java.net.InetAddress) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInetAddress(int, java.net.InetAddress) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setInetAddress(java.lang.String, java.net.InetAddress)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setInetAddress(java.lang.String, java.net.InetAddress)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInstant(int, java.time.Instant) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInstant(int, java.time.Instant) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setInstant(java.lang.String, java.time.Instant)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setInstant(java.lang.String, java.time.Instant)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInt(int, int) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInt(int, int) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setInt(java.lang.String, int)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setInt(java.lang.String, int)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setList(int, java.util.List, java.lang.Class) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setList(int, java.util.List, java.lang.Class) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setList(java.lang.String, java.util.List, java.lang.Class)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setList(java.lang.String, java.util.List, java.lang.Class)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLocalDate(int, java.time.LocalDate) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLocalDate(int, java.time.LocalDate) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setLocalDate(java.lang.String, java.time.LocalDate)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setLocalDate(java.lang.String, java.time.LocalDate)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLocalTime(int, java.time.LocalTime) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLocalTime(int, java.time.LocalTime) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setLocalTime(java.lang.String, java.time.LocalTime)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setLocalTime(java.lang.String, java.time.LocalTime)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLong(int, long) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLong(int, long) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setLong(java.lang.String, long)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setLong(java.lang.String, long)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setMap(int, java.util.Map, java.lang.Class, java.lang.Class) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setMap(int, java.util.Map, java.lang.Class, java.lang.Class) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setMap(java.lang.String, java.util.Map, java.lang.Class, java.lang.Class)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setMap(java.lang.String, java.util.Map, java.lang.Class, java.lang.Class)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setSet(int, java.util.Set, java.lang.Class) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setSet(int, java.util.Set, java.lang.Class) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setSet(java.lang.String, java.util.Set, java.lang.Class)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setSet(java.lang.String, java.util.Set, java.lang.Class)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setShort(int, short) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setShort(int, short) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setShort(java.lang.String, short)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setShort(java.lang.String, short)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setString(int, java.lang.String) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setString(int, java.lang.String) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setString(java.lang.String, java.lang.String)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setString(java.lang.String, java.lang.String)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setToNull(int) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setToNull(int) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setToNull(java.lang.String)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setToNull(java.lang.String)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setToken(int, com.datastax.oss.driver.api.core.metadata.token.Token) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setToken(int, com.datastax.oss.driver.api.core.metadata.token.Token) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setToken(java.lang.String, com.datastax.oss.driver.api.core.metadata.token.Token)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setToken(java.lang.String, com.datastax.oss.driver.api.core.metadata.token.Token)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setTupleValue(int, com.datastax.oss.driver.api.core.data.TupleValue) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setTupleValue(int, com.datastax.oss.driver.api.core.data.TupleValue) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setTupleValue(java.lang.String, com.datastax.oss.driver.api.core.data.TupleValue)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setTupleValue(java.lang.String, com.datastax.oss.driver.api.core.data.TupleValue)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setUdtValue(int, com.datastax.oss.driver.api.core.data.UdtValue) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setUdtValue(int, com.datastax.oss.driver.api.core.data.UdtValue) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setUdtValue(java.lang.String, com.datastax.oss.driver.api.core.data.UdtValue)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setUdtValue(java.lang.String, com.datastax.oss.driver.api.core.data.UdtValue)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setUuid(int, java.util.UUID) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setUuid(int, java.util.UUID) @ com.datastax.oss.driver.api.core.data.SettableByName>>", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setUuid(java.lang.String, java.util.UUID)", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setUuid(java.lang.String, java.util.UUID)", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, com.datastax.oss.driver.api.core.type.codec.TypeCodec) @ com.datastax.oss.driver.api.core.data.TupleValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, com.datastax.oss.driver.api.core.type.codec.TypeCodec) @ com.datastax.oss.driver.api.core.data.TupleValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, com.datastax.oss.driver.api.core.type.reflect.GenericType) @ com.datastax.oss.driver.api.core.data.TupleValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, com.datastax.oss.driver.api.core.type.reflect.GenericType) @ com.datastax.oss.driver.api.core.data.TupleValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, java.lang.Class) @ com.datastax.oss.driver.api.core.data.TupleValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, java.lang.Class) @ com.datastax.oss.driver.api.core.data.TupleValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBigDecimal(int, java.math.BigDecimal) @ com.datastax.oss.driver.api.core.data.TupleValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBigDecimal(int, java.math.BigDecimal) @ com.datastax.oss.driver.api.core.data.TupleValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBigInteger(int, java.math.BigInteger) @ com.datastax.oss.driver.api.core.data.TupleValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBigInteger(int, java.math.BigInteger) @ com.datastax.oss.driver.api.core.data.TupleValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBoolean(int, boolean) @ com.datastax.oss.driver.api.core.data.TupleValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBoolean(int, boolean) @ com.datastax.oss.driver.api.core.data.TupleValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setByte(int, byte) @ com.datastax.oss.driver.api.core.data.TupleValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setByte(int, byte) @ com.datastax.oss.driver.api.core.data.TupleValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setByteBuffer(int, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.data.TupleValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setByteBuffer(int, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.data.TupleValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBytesUnsafe(int, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.data.TupleValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBytesUnsafe(int, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.data.TupleValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setCqlDuration(int, com.datastax.oss.driver.api.core.data.CqlDuration) @ com.datastax.oss.driver.api.core.data.TupleValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setCqlDuration(int, com.datastax.oss.driver.api.core.data.CqlDuration) @ com.datastax.oss.driver.api.core.data.TupleValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setDouble(int, double) @ com.datastax.oss.driver.api.core.data.TupleValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setDouble(int, double) @ com.datastax.oss.driver.api.core.data.TupleValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setFloat(int, float) @ com.datastax.oss.driver.api.core.data.TupleValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setFloat(int, float) @ com.datastax.oss.driver.api.core.data.TupleValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInetAddress(int, java.net.InetAddress) @ com.datastax.oss.driver.api.core.data.TupleValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInetAddress(int, java.net.InetAddress) @ com.datastax.oss.driver.api.core.data.TupleValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInstant(int, java.time.Instant) @ com.datastax.oss.driver.api.core.data.TupleValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInstant(int, java.time.Instant) @ com.datastax.oss.driver.api.core.data.TupleValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInt(int, int) @ com.datastax.oss.driver.api.core.data.TupleValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInt(int, int) @ com.datastax.oss.driver.api.core.data.TupleValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setList(int, java.util.List, java.lang.Class) @ com.datastax.oss.driver.api.core.data.TupleValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setList(int, java.util.List, java.lang.Class) @ com.datastax.oss.driver.api.core.data.TupleValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLocalDate(int, java.time.LocalDate) @ com.datastax.oss.driver.api.core.data.TupleValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLocalDate(int, java.time.LocalDate) @ com.datastax.oss.driver.api.core.data.TupleValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLocalTime(int, java.time.LocalTime) @ com.datastax.oss.driver.api.core.data.TupleValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLocalTime(int, java.time.LocalTime) @ com.datastax.oss.driver.api.core.data.TupleValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLong(int, long) @ com.datastax.oss.driver.api.core.data.TupleValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLong(int, long) @ com.datastax.oss.driver.api.core.data.TupleValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setMap(int, java.util.Map, java.lang.Class, java.lang.Class) @ com.datastax.oss.driver.api.core.data.TupleValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setMap(int, java.util.Map, java.lang.Class, java.lang.Class) @ com.datastax.oss.driver.api.core.data.TupleValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setSet(int, java.util.Set, java.lang.Class) @ com.datastax.oss.driver.api.core.data.TupleValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setSet(int, java.util.Set, java.lang.Class) @ com.datastax.oss.driver.api.core.data.TupleValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setShort(int, short) @ com.datastax.oss.driver.api.core.data.TupleValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setShort(int, short) @ com.datastax.oss.driver.api.core.data.TupleValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setString(int, java.lang.String) @ com.datastax.oss.driver.api.core.data.TupleValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setString(int, java.lang.String) @ com.datastax.oss.driver.api.core.data.TupleValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setToNull(int) @ com.datastax.oss.driver.api.core.data.TupleValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setToNull(int) @ com.datastax.oss.driver.api.core.data.TupleValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setToken(int, com.datastax.oss.driver.api.core.metadata.token.Token) @ com.datastax.oss.driver.api.core.data.TupleValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setToken(int, com.datastax.oss.driver.api.core.metadata.token.Token) @ com.datastax.oss.driver.api.core.data.TupleValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setTupleValue(int, com.datastax.oss.driver.api.core.data.TupleValue) @ com.datastax.oss.driver.api.core.data.TupleValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setTupleValue(int, com.datastax.oss.driver.api.core.data.TupleValue) @ com.datastax.oss.driver.api.core.data.TupleValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setUdtValue(int, com.datastax.oss.driver.api.core.data.UdtValue) @ com.datastax.oss.driver.api.core.data.TupleValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setUdtValue(int, com.datastax.oss.driver.api.core.data.UdtValue) @ com.datastax.oss.driver.api.core.data.TupleValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setUuid(int, java.util.UUID) @ com.datastax.oss.driver.api.core.data.TupleValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setUuid(int, java.util.UUID) @ com.datastax.oss.driver.api.core.data.TupleValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::set(com.datastax.oss.driver.api.core.CqlIdentifier, ValueT, com.datastax.oss.driver.api.core.type.codec.TypeCodec) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::set(com.datastax.oss.driver.api.core.CqlIdentifier, ValueT, com.datastax.oss.driver.api.core.type.codec.TypeCodec) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::set(com.datastax.oss.driver.api.core.CqlIdentifier, ValueT, com.datastax.oss.driver.api.core.type.reflect.GenericType) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::set(com.datastax.oss.driver.api.core.CqlIdentifier, ValueT, com.datastax.oss.driver.api.core.type.reflect.GenericType) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::set(com.datastax.oss.driver.api.core.CqlIdentifier, ValueT, java.lang.Class) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::set(com.datastax.oss.driver.api.core.CqlIdentifier, ValueT, java.lang.Class) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, com.datastax.oss.driver.api.core.type.codec.TypeCodec) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, com.datastax.oss.driver.api.core.type.codec.TypeCodec) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, com.datastax.oss.driver.api.core.type.reflect.GenericType) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, com.datastax.oss.driver.api.core.type.reflect.GenericType) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, java.lang.Class) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::set(int, ValueT, java.lang.Class) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::set(java.lang.String, ValueT, com.datastax.oss.driver.api.core.type.codec.TypeCodec) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::set(java.lang.String, ValueT, com.datastax.oss.driver.api.core.type.codec.TypeCodec) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::set(java.lang.String, ValueT, com.datastax.oss.driver.api.core.type.reflect.GenericType) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::set(java.lang.String, ValueT, com.datastax.oss.driver.api.core.type.reflect.GenericType) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::set(java.lang.String, ValueT, java.lang.Class) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::set(java.lang.String, ValueT, java.lang.Class) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setBigDecimal(com.datastax.oss.driver.api.core.CqlIdentifier, java.math.BigDecimal) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setBigDecimal(com.datastax.oss.driver.api.core.CqlIdentifier, java.math.BigDecimal) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBigDecimal(int, java.math.BigDecimal) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBigDecimal(int, java.math.BigDecimal) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setBigDecimal(java.lang.String, java.math.BigDecimal) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setBigDecimal(java.lang.String, java.math.BigDecimal) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setBigInteger(com.datastax.oss.driver.api.core.CqlIdentifier, java.math.BigInteger) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setBigInteger(com.datastax.oss.driver.api.core.CqlIdentifier, java.math.BigInteger) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBigInteger(int, java.math.BigInteger) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBigInteger(int, java.math.BigInteger) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setBigInteger(java.lang.String, java.math.BigInteger) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setBigInteger(java.lang.String, java.math.BigInteger) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setBoolean(com.datastax.oss.driver.api.core.CqlIdentifier, boolean) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setBoolean(com.datastax.oss.driver.api.core.CqlIdentifier, boolean) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBoolean(int, boolean) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBoolean(int, boolean) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setBoolean(java.lang.String, boolean) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setBoolean(java.lang.String, boolean) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setByte(com.datastax.oss.driver.api.core.CqlIdentifier, byte) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setByte(com.datastax.oss.driver.api.core.CqlIdentifier, byte) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setByte(int, byte) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setByte(int, byte) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setByte(java.lang.String, byte) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setByte(java.lang.String, byte) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setByteBuffer(com.datastax.oss.driver.api.core.CqlIdentifier, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setByteBuffer(com.datastax.oss.driver.api.core.CqlIdentifier, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setByteBuffer(int, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setByteBuffer(int, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setByteBuffer(java.lang.String, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setByteBuffer(java.lang.String, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setBytesUnsafe(com.datastax.oss.driver.api.core.CqlIdentifier, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setBytesUnsafe(com.datastax.oss.driver.api.core.CqlIdentifier, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBytesUnsafe(int, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setBytesUnsafe(int, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setBytesUnsafe(java.lang.String, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setBytesUnsafe(java.lang.String, java.nio.ByteBuffer) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setCqlDuration(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.data.CqlDuration) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setCqlDuration(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.data.CqlDuration) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setCqlDuration(int, com.datastax.oss.driver.api.core.data.CqlDuration) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setCqlDuration(int, com.datastax.oss.driver.api.core.data.CqlDuration) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setCqlDuration(java.lang.String, com.datastax.oss.driver.api.core.data.CqlDuration) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setCqlDuration(java.lang.String, com.datastax.oss.driver.api.core.data.CqlDuration) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setDouble(com.datastax.oss.driver.api.core.CqlIdentifier, double) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setDouble(com.datastax.oss.driver.api.core.CqlIdentifier, double) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setDouble(int, double) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setDouble(int, double) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setDouble(java.lang.String, double) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setDouble(java.lang.String, double) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setFloat(com.datastax.oss.driver.api.core.CqlIdentifier, float) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setFloat(com.datastax.oss.driver.api.core.CqlIdentifier, float) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setFloat(int, float) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setFloat(int, float) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setFloat(java.lang.String, float) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setFloat(java.lang.String, float) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setInetAddress(com.datastax.oss.driver.api.core.CqlIdentifier, java.net.InetAddress) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setInetAddress(com.datastax.oss.driver.api.core.CqlIdentifier, java.net.InetAddress) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInetAddress(int, java.net.InetAddress) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInetAddress(int, java.net.InetAddress) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setInetAddress(java.lang.String, java.net.InetAddress) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setInetAddress(java.lang.String, java.net.InetAddress) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setInstant(com.datastax.oss.driver.api.core.CqlIdentifier, java.time.Instant) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setInstant(com.datastax.oss.driver.api.core.CqlIdentifier, java.time.Instant) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInstant(int, java.time.Instant) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInstant(int, java.time.Instant) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setInstant(java.lang.String, java.time.Instant) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setInstant(java.lang.String, java.time.Instant) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setInt(com.datastax.oss.driver.api.core.CqlIdentifier, int) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setInt(com.datastax.oss.driver.api.core.CqlIdentifier, int) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInt(int, int) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setInt(int, int) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setInt(java.lang.String, int) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setInt(java.lang.String, int) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setList(com.datastax.oss.driver.api.core.CqlIdentifier, java.util.List, java.lang.Class) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setList(com.datastax.oss.driver.api.core.CqlIdentifier, java.util.List, java.lang.Class) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setList(int, java.util.List, java.lang.Class) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setList(int, java.util.List, java.lang.Class) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setList(java.lang.String, java.util.List, java.lang.Class) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setList(java.lang.String, java.util.List, java.lang.Class) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setLocalDate(com.datastax.oss.driver.api.core.CqlIdentifier, java.time.LocalDate) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setLocalDate(com.datastax.oss.driver.api.core.CqlIdentifier, java.time.LocalDate) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLocalDate(int, java.time.LocalDate) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLocalDate(int, java.time.LocalDate) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setLocalDate(java.lang.String, java.time.LocalDate) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setLocalDate(java.lang.String, java.time.LocalDate) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setLocalTime(com.datastax.oss.driver.api.core.CqlIdentifier, java.time.LocalTime) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setLocalTime(com.datastax.oss.driver.api.core.CqlIdentifier, java.time.LocalTime) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLocalTime(int, java.time.LocalTime) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLocalTime(int, java.time.LocalTime) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setLocalTime(java.lang.String, java.time.LocalTime) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setLocalTime(java.lang.String, java.time.LocalTime) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setLong(com.datastax.oss.driver.api.core.CqlIdentifier, long) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setLong(com.datastax.oss.driver.api.core.CqlIdentifier, long) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLong(int, long) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setLong(int, long) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setLong(java.lang.String, long) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setLong(java.lang.String, long) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setMap(com.datastax.oss.driver.api.core.CqlIdentifier, java.util.Map, java.lang.Class, java.lang.Class) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setMap(com.datastax.oss.driver.api.core.CqlIdentifier, java.util.Map, java.lang.Class, java.lang.Class) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setMap(int, java.util.Map, java.lang.Class, java.lang.Class) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setMap(int, java.util.Map, java.lang.Class, java.lang.Class) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setMap(java.lang.String, java.util.Map, java.lang.Class, java.lang.Class) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setMap(java.lang.String, java.util.Map, java.lang.Class, java.lang.Class) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setSet(com.datastax.oss.driver.api.core.CqlIdentifier, java.util.Set, java.lang.Class) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setSet(com.datastax.oss.driver.api.core.CqlIdentifier, java.util.Set, java.lang.Class) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setSet(int, java.util.Set, java.lang.Class) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setSet(int, java.util.Set, java.lang.Class) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setSet(java.lang.String, java.util.Set, java.lang.Class) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setSet(java.lang.String, java.util.Set, java.lang.Class) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setShort(com.datastax.oss.driver.api.core.CqlIdentifier, short) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setShort(com.datastax.oss.driver.api.core.CqlIdentifier, short) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setShort(int, short) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setShort(int, short) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setShort(java.lang.String, short) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setShort(java.lang.String, short) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setString(com.datastax.oss.driver.api.core.CqlIdentifier, java.lang.String) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setString(com.datastax.oss.driver.api.core.CqlIdentifier, java.lang.String) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setString(int, java.lang.String) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setString(int, java.lang.String) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setString(java.lang.String, java.lang.String) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setString(java.lang.String, java.lang.String) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setToNull(com.datastax.oss.driver.api.core.CqlIdentifier) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setToNull(com.datastax.oss.driver.api.core.CqlIdentifier) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setToNull(int) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setToNull(int) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setToNull(java.lang.String) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setToNull(java.lang.String) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setToken(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.metadata.token.Token) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setToken(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.metadata.token.Token) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setToken(int, com.datastax.oss.driver.api.core.metadata.token.Token) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setToken(int, com.datastax.oss.driver.api.core.metadata.token.Token) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setToken(java.lang.String, com.datastax.oss.driver.api.core.metadata.token.Token) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setToken(java.lang.String, com.datastax.oss.driver.api.core.metadata.token.Token) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setTupleValue(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.data.TupleValue) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setTupleValue(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.data.TupleValue) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setTupleValue(int, com.datastax.oss.driver.api.core.data.TupleValue) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setTupleValue(int, com.datastax.oss.driver.api.core.data.TupleValue) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setTupleValue(java.lang.String, com.datastax.oss.driver.api.core.data.TupleValue) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setTupleValue(java.lang.String, com.datastax.oss.driver.api.core.data.TupleValue) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setUdtValue(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.data.UdtValue) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setUdtValue(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.data.UdtValue) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setUdtValue(int, com.datastax.oss.driver.api.core.data.UdtValue) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setUdtValue(int, com.datastax.oss.driver.api.core.data.UdtValue) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setUdtValue(java.lang.String, com.datastax.oss.driver.api.core.data.UdtValue) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setUdtValue(java.lang.String, com.datastax.oss.driver.api.core.data.UdtValue) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setUuid(com.datastax.oss.driver.api.core.CqlIdentifier, java.util.UUID) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableById>>::setUuid(com.datastax.oss.driver.api.core.CqlIdentifier, java.util.UUID) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setUuid(int, java.util.UUID) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByIndex>>::setUuid(int, java.util.UUID) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setUuid(java.lang.String, java.util.UUID) @ com.datastax.oss.driver.api.core.data.UdtValue", - "new": "method SelfT com.datastax.oss.driver.api.core.data.SettableByName>>::setUuid(java.lang.String, java.util.UUID) @ com.datastax.oss.driver.api.core.data.UdtValue", - "annotation": "@edu.umd.cs.findbugs.annotations.CheckReturnValue", - "justification": "JAVA-2161: Annotate mutating methods with @CheckReturnValue" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setConsistencyLevel(com.datastax.oss.driver.api.core.ConsistencyLevel) @ com.datastax.oss.driver.api.core.cql.BatchStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setConsistencyLevel(com.datastax.oss.driver.api.core.ConsistencyLevel) @ com.datastax.oss.driver.api.core.cql.BatchStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.NonNull", - "justification": "Add missing `@NonNull` annotation to Statement.setConsistencyLevel" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setConsistencyLevel(com.datastax.oss.driver.api.core.ConsistencyLevel) @ com.datastax.oss.driver.api.core.cql.BatchableStatement>>", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setConsistencyLevel(com.datastax.oss.driver.api.core.ConsistencyLevel) @ com.datastax.oss.driver.api.core.cql.BatchableStatement>>", - "annotation": "@edu.umd.cs.findbugs.annotations.NonNull", - "justification": "Add missing `@NonNull` annotation to Statement.setConsistencyLevel" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setConsistencyLevel(com.datastax.oss.driver.api.core.ConsistencyLevel) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setConsistencyLevel(com.datastax.oss.driver.api.core.ConsistencyLevel) @ com.datastax.oss.driver.api.core.cql.BoundStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.NonNull", - "justification": "Add missing `@NonNull` annotation to Statement.setConsistencyLevel" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setConsistencyLevel(com.datastax.oss.driver.api.core.ConsistencyLevel) @ com.datastax.oss.driver.api.core.cql.SimpleStatement", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setConsistencyLevel(com.datastax.oss.driver.api.core.ConsistencyLevel) @ com.datastax.oss.driver.api.core.cql.SimpleStatement", - "annotation": "@edu.umd.cs.findbugs.annotations.NonNull", - "justification": "Add missing `@NonNull` annotation to Statement.setConsistencyLevel" - }, - { - "code": "java.annotation.added", - "old": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setConsistencyLevel(com.datastax.oss.driver.api.core.ConsistencyLevel)", - "new": "method SelfT com.datastax.oss.driver.api.core.cql.Statement>>::setConsistencyLevel(com.datastax.oss.driver.api.core.ConsistencyLevel)", - "annotation": "@edu.umd.cs.findbugs.annotations.NonNull", - "justification": "Add missing `@NonNull` annotation to Statement.setConsistencyLevel" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.concurrent.CompletionStage> com.datastax.oss.driver.api.core.AsyncPagingIterable::fetchNextPage() throws java.lang.IllegalStateException", - "new": "method java.util.concurrent.CompletionStage com.datastax.oss.driver.api.core.AsyncPagingIterable>>::fetchNextPage() throws java.lang.IllegalStateException", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeChangedCovariantly", - "old": "method com.datastax.oss.driver.api.core.AsyncPagingIterable com.datastax.oss.driver.api.core.AsyncPagingIterable::map(java.util.function.Function)", - "new": "method com.datastax.oss.driver.api.core.MappedAsyncPagingIterable com.datastax.oss.driver.api.core.AsyncPagingIterable>>::map(java.util.function.Function)", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.generics.formalTypeParameterAdded", - "old": "interface com.datastax.oss.driver.api.core.AsyncPagingIterable", - "new": "interface com.datastax.oss.driver.api.core.AsyncPagingIterable>", - "typeParameter": "SelfT extends com.datastax.oss.driver.api.core.AsyncPagingIterable>", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.concurrent.CompletionStage com.datastax.oss.driver.api.core.CqlSession::executeAsync(com.datastax.oss.driver.api.core.cql.Statement)", - "new": "method java.util.concurrent.CompletionStage com.datastax.oss.driver.api.core.CqlSession::executeAsync(com.datastax.oss.driver.api.core.cql.Statement)", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.concurrent.CompletionStage com.datastax.oss.driver.api.core.CqlSession::executeAsync(java.lang.String)", - "new": "method java.util.concurrent.CompletionStage com.datastax.oss.driver.api.core.CqlSession::executeAsync(java.lang.String)", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.concurrent.CompletionStage com.datastax.oss.driver.api.core.CqlSession::prepareAsync(com.datastax.oss.driver.api.core.cql.PrepareRequest)", - "new": "method java.util.concurrent.CompletionStage com.datastax.oss.driver.api.core.CqlSession::prepareAsync(com.datastax.oss.driver.api.core.cql.PrepareRequest)", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.concurrent.CompletionStage com.datastax.oss.driver.api.core.CqlSession::prepareAsync(com.datastax.oss.driver.api.core.cql.SimpleStatement)", - "new": "method java.util.concurrent.CompletionStage com.datastax.oss.driver.api.core.CqlSession::prepareAsync(com.datastax.oss.driver.api.core.cql.SimpleStatement)", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.concurrent.CompletionStage com.datastax.oss.driver.api.core.CqlSession::prepareAsync(java.lang.String)", - "new": "method java.util.concurrent.CompletionStage com.datastax.oss.driver.api.core.CqlSession::prepareAsync(java.lang.String)", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.concurrent.CompletionStage com.datastax.oss.driver.api.core.cql.AsyncResultSet::fetchNextPage() throws java.lang.IllegalStateException", - "new": "method java.util.concurrent.CompletionStage com.datastax.oss.driver.api.core.AsyncPagingIterable>>::fetchNextPage() throws java.lang.IllegalStateException @ com.datastax.oss.driver.api.core.cql.AsyncResultSet", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.class.noLongerImplementsInterface", - "old": "interface com.datastax.oss.driver.api.core.cql.AsyncResultSet", - "new": "interface com.datastax.oss.driver.api.core.cql.AsyncResultSet", - "interface": "com.datastax.oss.driver.api.core.AsyncPagingIterable", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.class.superTypeTypeParametersChanged", - "old": "interface com.datastax.oss.driver.api.core.cql.AsyncResultSet", - "new": "interface com.datastax.oss.driver.api.core.cql.AsyncResultSet", - "oldSuperType": "com.datastax.oss.driver.api.core.AsyncPagingIterable", - "newSuperType": "com.datastax.oss.driver.api.core.AsyncPagingIterable", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.concurrent.CompletionStage com.datastax.oss.driver.api.core.session.Session::refreshSchemaAsync()", - "new": "method java.util.concurrent.CompletionStage com.datastax.oss.driver.api.core.session.Session::refreshSchemaAsync()", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.concurrent.CompletionStage com.datastax.oss.driver.api.core.session.Session::setSchemaMetadataEnabled(java.lang.Boolean)", - "new": "method java.util.concurrent.CompletionStage com.datastax.oss.driver.api.core.session.Session::setSchemaMetadataEnabled(java.lang.Boolean)", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.Optional com.datastax.oss.driver.api.core.session.Session::getMetrics()", - "new": "method java.util.Optional com.datastax.oss.driver.api.core.session.Session::getMetrics()", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.Metadata::getKeyspace(com.datastax.oss.driver.api.core.CqlIdentifier)", - "new": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.Metadata::getKeyspace(com.datastax.oss.driver.api.core.CqlIdentifier)", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.Metadata::getKeyspace(java.lang.String)", - "new": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.Metadata::getKeyspace(java.lang.String)", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.Map com.datastax.oss.driver.api.core.metadata.Metadata::getKeyspaces()", - "new": "method java.util.Map com.datastax.oss.driver.api.core.metadata.Metadata::getKeyspaces()", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.Metadata::getTokenMap()", - "new": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.Metadata::getTokenMap()", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getAggregate(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.type.DataType[])", - "new": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getAggregate(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.type.DataType[])", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getAggregate(com.datastax.oss.driver.api.core.CqlIdentifier, java.lang.Iterable)", - "new": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getAggregate(com.datastax.oss.driver.api.core.CqlIdentifier, java.lang.Iterable)", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getAggregate(com.datastax.oss.driver.api.core.metadata.schema.FunctionSignature)", - "new": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getAggregate(com.datastax.oss.driver.api.core.metadata.schema.FunctionSignature)", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getAggregate(java.lang.String, com.datastax.oss.driver.api.core.type.DataType[])", - "new": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getAggregate(java.lang.String, com.datastax.oss.driver.api.core.type.DataType[])", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getAggregate(java.lang.String, java.lang.Iterable)", - "new": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getAggregate(java.lang.String, java.lang.Iterable)", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.Map com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getAggregates()", - "new": "method java.util.Map com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getAggregates()", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getFunction(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.type.DataType[])", - "new": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getFunction(com.datastax.oss.driver.api.core.CqlIdentifier, com.datastax.oss.driver.api.core.type.DataType[])", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getFunction(com.datastax.oss.driver.api.core.CqlIdentifier, java.lang.Iterable)", - "new": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getFunction(com.datastax.oss.driver.api.core.CqlIdentifier, java.lang.Iterable)", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getFunction(com.datastax.oss.driver.api.core.metadata.schema.FunctionSignature)", - "new": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getFunction(com.datastax.oss.driver.api.core.metadata.schema.FunctionSignature)", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getFunction(java.lang.String, com.datastax.oss.driver.api.core.type.DataType[])", - "new": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getFunction(java.lang.String, com.datastax.oss.driver.api.core.type.DataType[])", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getFunction(java.lang.String, java.lang.Iterable)", - "new": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getFunction(java.lang.String, java.lang.Iterable)", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.Map com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getFunctions()", - "new": "method java.util.Map com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getFunctions()", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getTable(com.datastax.oss.driver.api.core.CqlIdentifier)", - "new": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getTable(com.datastax.oss.driver.api.core.CqlIdentifier)", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getTable(java.lang.String)", - "new": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getTable(java.lang.String)", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.Map com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getTables()", - "new": "method java.util.Map com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getTables()", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getUserDefinedType(com.datastax.oss.driver.api.core.CqlIdentifier)", - "new": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getUserDefinedType(com.datastax.oss.driver.api.core.CqlIdentifier)", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getUserDefinedType(java.lang.String)", - "new": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getUserDefinedType(java.lang.String)", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.Map com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getUserDefinedTypes()", - "new": "method java.util.Map com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getUserDefinedTypes()", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getView(com.datastax.oss.driver.api.core.CqlIdentifier)", - "new": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getView(com.datastax.oss.driver.api.core.CqlIdentifier)", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getView(java.lang.String)", - "new": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getView(java.lang.String)", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.Map com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getViews()", - "new": "method java.util.Map com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getViews()", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.Map com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getViewsOnTable(com.datastax.oss.driver.api.core.CqlIdentifier)", - "new": "method java.util.Map com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata::getViewsOnTable(com.datastax.oss.driver.api.core.CqlIdentifier)", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.Map com.datastax.oss.driver.api.core.metadata.schema.RelationMetadata::getClusteringColumns()", - "new": "method java.util.Map com.datastax.oss.driver.api.core.metadata.schema.RelationMetadata::getClusteringColumns()", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.schema.RelationMetadata::getColumn(com.datastax.oss.driver.api.core.CqlIdentifier)", - "new": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.schema.RelationMetadata::getColumn(com.datastax.oss.driver.api.core.CqlIdentifier)", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.schema.RelationMetadata::getColumn(java.lang.String)", - "new": "method java.util.Optional com.datastax.oss.driver.api.core.metadata.schema.RelationMetadata::getColumn(java.lang.String)", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.Map com.datastax.oss.driver.api.core.metadata.schema.RelationMetadata::getColumns()", - "new": "method java.util.Map com.datastax.oss.driver.api.core.metadata.schema.RelationMetadata::getColumns()", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.List com.datastax.oss.driver.api.core.metadata.schema.RelationMetadata::getPartitionKey()", - "new": "method java.util.List com.datastax.oss.driver.api.core.metadata.schema.RelationMetadata::getPartitionKey()", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.List com.datastax.oss.driver.api.core.metadata.schema.RelationMetadata::getPrimaryKey()", - "new": "method java.util.List com.datastax.oss.driver.api.core.metadata.schema.RelationMetadata::getPrimaryKey()", - "justification": "JAVA-2192: Don't return generic types with wildcards" - }, - { - "code": "java.method.returnTypeTypeParametersChanged", - "old": "method java.util.Map com.datastax.oss.driver.api.core.metadata.schema.TableMetadata::getIndexes()", - "new": "method java.util.Map com.datastax.oss.driver.api.core.metadata.schema.TableMetadata::getIndexes()", - "justification": "JAVA-2192: Don't return generic types with wildcards" + "ignore": true, + "code": "java.method.numberOfParametersChanged", + "old": "method java.util.List com.fasterxml.jackson.databind.type.TypeParser::parseTypes(com.fasterxml.jackson.databind.type.TypeParser.MyTokenizer) throws java.lang.IllegalArgumentException", + "new": "method java.util.List com.fasterxml.jackson.databind.type.TypeParser::parseTypes(com.fasterxml.jackson.databind.type.TypeParser.MyTokenizer, int) throws java.lang.IllegalArgumentException", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method com.datastax.oss.driver.api.core.config.DriverExecutionProfile com.datastax.oss.driver.api.core.config.DriverExecutionProfile::withLong(com.datastax.oss.driver.api.core.config.DriverOption, long)", - "new": "method SelfT com.datastax.oss.driver.api.core.config.OngoingConfigOptions>>::withLong(com.datastax.oss.driver.api.core.config.DriverOption, long) @ com.datastax.oss.driver.api.core.config.DriverExecutionProfile", - "annotation": "@edu.umd.cs.findbugs.annotations.NonNull", - "justification": "Bugfix, the annotation should have been present from the beginning" + "ignore": true, + "code": "java.field.removed", + "old": "field com.fasterxml.jackson.databind.util.LRUMap._jdkSerializeMaxEntries", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method java.lang.String com.datastax.oss.driver.api.core.metadata.EndPoint::asMetricPrefix()", - "new": "method java.lang.String com.datastax.oss.driver.api.core.metadata.EndPoint::asMetricPrefix()", - "annotation": "@edu.umd.cs.findbugs.annotations.NonNull", - "justification": "EndPoint.asMetricPrefix() was missing @NonNull" + "ignore": true, + "code": "java.field.typeChanged", + "old": "field com.fasterxml.jackson.databind.util.LRUMap._map", + "new": "field com.fasterxml.jackson.databind.util.LRUMap._map", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" }, { - "code": "java.annotation.added", - "old": "method java.net.SocketAddress com.datastax.oss.driver.api.core.metadata.EndPoint::resolve()", - "new": "method java.net.SocketAddress com.datastax.oss.driver.api.core.metadata.EndPoint::resolve()", - "annotation": "@edu.umd.cs.findbugs.annotations.NonNull", - "justification": "EndPoint.resolve() was missing @NonNull" + "ignore": true, + "code": "java.field.serialVersionUIDChanged", + "old": "field com.fasterxml.jackson.databind.util.LRUMap.serialVersionUID", + "new": "field com.fasterxml.jackson.databind.util.LRUMap.serialVersionUID", + "oldSerialVersionUID": "1", + "newSerialVersionUID": "2", + "justification": "ADD YOUR EXPLANATION FOR THE NECESSITY OF THIS CHANGE" } ] } diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/DseProtocolVersion.java b/core/src/main/java/com/datastax/dse/driver/api/core/DseProtocolVersion.java new file mode 100644 index 00000000000..dc420970427 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/DseProtocolVersion.java @@ -0,0 +1,59 @@ +/* + * 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 com.datastax.dse.driver.api.core; + +import com.datastax.dse.protocol.internal.DseProtocolConstants; +import com.datastax.oss.driver.api.core.DefaultProtocolVersion; +import com.datastax.oss.driver.api.core.ProtocolVersion; + +/** + * A DSE-specific protocol version. + * + *

Legacy DSE versions did not have a specific version, but instead reused a Cassandra protocol + * version: DSE 5.0 is supported via {@link DefaultProtocolVersion#V4}, and DSE 4.7 and 4.8 via + * {@link DefaultProtocolVersion#V3}. + * + *

DSE 4.6 and earlier are not supported by this version of the driver, use the 1.x series. + */ +public enum DseProtocolVersion implements ProtocolVersion { + + /** Version 1, supported by DSE 5.1.0 and above. */ + DSE_V1(DseProtocolConstants.Version.DSE_V1, false), + + /** Version 2, supported by DSE 6 and above. */ + DSE_V2(DseProtocolConstants.Version.DSE_V2, false), + ; + + private final int code; + private final boolean beta; + + DseProtocolVersion(int code, boolean beta) { + this.code = code; + this.beta = beta; + } + + @Override + public int getCode() { + return code; + } + + @Override + public boolean isBeta() { + return beta; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/DseSession.java b/core/src/main/java/com/datastax/dse/driver/api/core/DseSession.java new file mode 100644 index 00000000000..8251aaf767c --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/DseSession.java @@ -0,0 +1,47 @@ +/* + * 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 com.datastax.dse.driver.api.core; + +import com.datastax.oss.driver.api.core.CqlSession; +import com.datastax.oss.driver.api.core.MavenCoordinates; +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * @deprecated All DSE functionality is now available directly on {@link CqlSession}. This type is + * preserved for backward compatibility, but you should now use {@link CqlSession} instead. + */ +@Deprecated +public interface DseSession extends CqlSession { + + /** + * @deprecated the DSE driver is no longer published as a separate artifact. This field is + * preserved for backward compatibility, but it returns the same value as {@link + * CqlSession#OSS_DRIVER_COORDINATES}. + */ + @Deprecated @NonNull MavenCoordinates DSE_DRIVER_COORDINATES = CqlSession.OSS_DRIVER_COORDINATES; + + /** + * Returns a builder to create a new instance. + * + *

Note that this builder is mutable and not thread-safe. + */ + @NonNull + static DseSessionBuilder builder() { + return new DseSessionBuilder(); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/DseSessionBuilder.java b/core/src/main/java/com/datastax/dse/driver/api/core/DseSessionBuilder.java new file mode 100644 index 00000000000..01e5f9f9125 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/DseSessionBuilder.java @@ -0,0 +1,39 @@ +/* + * 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 com.datastax.dse.driver.api.core; + +import com.datastax.oss.driver.api.core.CqlSession; +import com.datastax.oss.driver.api.core.session.SessionBuilder; +import edu.umd.cs.findbugs.annotations.NonNull; +import net.jcip.annotations.NotThreadSafe; + +/** + * @deprecated DSE functionality is now exposed directly on {@link CqlSession}. This class is + * preserved for backward compatibility, but {@link CqlSession#builder()} should be used + * instead. + */ +@NotThreadSafe +@Deprecated +public class DseSessionBuilder extends SessionBuilder { + + @NonNull + @Override + protected DseSession wrap(@NonNull CqlSession defaultSession) { + return new com.datastax.dse.driver.internal.core.session.DefaultDseSession(defaultSession); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/auth/BaseDseAuthenticator.java b/core/src/main/java/com/datastax/dse/driver/api/core/auth/BaseDseAuthenticator.java new file mode 100644 index 00000000000..abd68b530b6 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/auth/BaseDseAuthenticator.java @@ -0,0 +1,92 @@ +/* + * 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 com.datastax.dse.driver.api.core.auth; + +import com.datastax.oss.driver.api.core.auth.SyncAuthenticator; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.nio.ByteBuffer; +import net.jcip.annotations.ThreadSafe; + +/** + * Base class for {@link SyncAuthenticator} implementations that want to make use of the + * authentication scheme negotiation in DseAuthenticator. + */ +@ThreadSafe +public abstract class BaseDseAuthenticator implements SyncAuthenticator { + + private static final String DSE_AUTHENTICATOR = + "com.datastax.bdp.cassandra.auth.DseAuthenticator"; + + private final String serverAuthenticator; + + protected BaseDseAuthenticator(@NonNull String serverAuthenticator) { + this.serverAuthenticator = serverAuthenticator; + } + + /** + * Return a byte buffer containing the required SASL mechanism. + * + *

This should be one of: + * + *

    + *
  • PLAIN + *
  • GSSAPI + *
+ * + * This must be either a {@linkplain ByteBuffer#asReadOnlyBuffer() read-only} buffer, or a new + * instance every time. + */ + @NonNull + protected abstract ByteBuffer getMechanism(); + + /** + * Return a byte buffer containing the expected successful server challenge. + * + *

This should be one of: + * + *

    + *
  • PLAIN-START + *
  • GSSAPI-START + *
+ * + * This must be either a {@linkplain ByteBuffer#asReadOnlyBuffer() read-only} buffer, or a new + * instance every time. + */ + @NonNull + protected abstract ByteBuffer getInitialServerChallenge(); + + @Nullable + @Override + public ByteBuffer initialResponseSync() { + // DseAuthenticator communicates back the mechanism in response to server authenticate message. + // older authenticators simply expect the auth response with credentials. + if (isDseAuthenticator()) { + return getMechanism(); + } else { + return evaluateChallengeSync(getInitialServerChallenge()); + } + } + + @Override + public void onAuthenticationSuccessSync(@Nullable ByteBuffer token) {} + + private boolean isDseAuthenticator() { + return serverAuthenticator.equals(DSE_AUTHENTICATOR); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/auth/DseGssApiAuthProviderBase.java b/core/src/main/java/com/datastax/dse/driver/api/core/auth/DseGssApiAuthProviderBase.java new file mode 100644 index 00000000000..48a0e5b0ef3 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/auth/DseGssApiAuthProviderBase.java @@ -0,0 +1,378 @@ +/* + * 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 com.datastax.dse.driver.api.core.auth; + +import com.datastax.oss.driver.api.core.auth.AuthProvider; +import com.datastax.oss.driver.api.core.auth.AuthenticationException; +import com.datastax.oss.driver.api.core.auth.Authenticator; +import com.datastax.oss.driver.api.core.metadata.EndPoint; +import com.datastax.oss.driver.api.core.session.Session; +import com.datastax.oss.driver.shaded.guava.common.base.Charsets; +import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableMap; +import com.datastax.oss.protocol.internal.util.Bytes; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import javax.security.auth.Subject; +import javax.security.auth.login.AppConfigurationEntry; +import javax.security.auth.login.Configuration; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslException; +import net.jcip.annotations.Immutable; +import net.jcip.annotations.NotThreadSafe; +import net.jcip.annotations.ThreadSafe; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@ThreadSafe +public abstract class DseGssApiAuthProviderBase implements AuthProvider { + + /** The default SASL service name used by this auth provider. */ + public static final String DEFAULT_SASL_SERVICE_NAME = "dse"; + + /** The name of the system property to use to specify the SASL service name. */ + public static final String SASL_SERVICE_NAME_PROPERTY = "dse.sasl.service"; + + /** + * Legacy system property for SASL protocol name. Clients should migrate to + * SASL_SERVICE_NAME_PROPERTY above. + */ + private static final String LEGACY_SASL_PROTOCOL_PROPERTY = "dse.sasl.protocol"; + + private static final Logger LOG = LoggerFactory.getLogger(DseGssApiAuthProviderBase.class); + + private final String logPrefix; + + /** + * @param logPrefix a string that will get prepended to the logs (this is used for discrimination + * when you have multiple driver instances executing in the same JVM). Config-based + * implementations fill this with {@link Session#getName()}. + */ + protected DseGssApiAuthProviderBase(@NonNull String logPrefix) { + this.logPrefix = Objects.requireNonNull(logPrefix); + } + + @NonNull + protected abstract GssApiOptions getOptions( + @NonNull EndPoint endPoint, @NonNull String serverAuthenticator); + + @NonNull + @Override + public Authenticator newAuthenticator( + @NonNull EndPoint endPoint, @NonNull String serverAuthenticator) + throws AuthenticationException { + return new GssApiAuthenticator( + getOptions(endPoint, serverAuthenticator), endPoint, serverAuthenticator); + } + + @Override + public void onMissingChallenge(@NonNull EndPoint endPoint) { + LOG.warn( + "[{}] {} did not send an authentication challenge; " + + "This is suspicious because the driver expects authentication", + logPrefix, + endPoint); + } + + @Override + public void close() { + // nothing to do + } + + /** + * The options to initialize a new authenticator. + * + *

Use {@link #builder()} to create an instance. + */ + @Immutable + public static class GssApiOptions { + + @NonNull + public static Builder builder() { + return new Builder(); + } + + private final Configuration loginConfiguration; + private final Subject subject; + private final String saslProtocol; + private final String authorizationId; + private final Map saslProperties; + + private GssApiOptions( + @Nullable Configuration loginConfiguration, + @Nullable Subject subject, + @Nullable String saslProtocol, + @Nullable String authorizationId, + @NonNull Map saslProperties) { + this.loginConfiguration = loginConfiguration; + this.subject = subject; + this.saslProtocol = saslProtocol; + this.authorizationId = authorizationId; + this.saslProperties = saslProperties; + } + + @Nullable + public Configuration getLoginConfiguration() { + return loginConfiguration; + } + + @Nullable + public Subject getSubject() { + return subject; + } + + @Nullable + public String getSaslProtocol() { + return saslProtocol; + } + + @Nullable + public String getAuthorizationId() { + return authorizationId; + } + + @NonNull + public Map getSaslProperties() { + return saslProperties; + } + + @NotThreadSafe + public static class Builder { + + private Configuration loginConfiguration; + private Subject subject; + private String saslProtocol; + private String authorizationId; + private final Map saslProperties = new HashMap<>(); + + public Builder() { + saslProperties.put(Sasl.SERVER_AUTH, "true"); + saslProperties.put(Sasl.QOP, "auth"); + } + + /** + * Sets a login configuration that will be used to create a {@link LoginContext}. + * + *

You MUST call either a withLoginConfiguration method or {@link #withSubject(Subject)}; + * if both are called, the subject takes precedence, and the login configuration will be + * ignored. + * + * @see #withLoginConfiguration(Map) + */ + @NonNull + public Builder withLoginConfiguration(@Nullable Configuration loginConfiguration) { + this.loginConfiguration = loginConfiguration; + return this; + } + /** + * Sets a login configuration that will be used to create a {@link LoginContext}. + * + *

This is an alternative to {@link #withLoginConfiguration(Configuration)}, that builds + * the configuration from {@code Krb5LoginModule} with the given options. + * + *

You MUST call either a withLoginConfiguration method or {@link #withSubject(Subject)}; + * if both are called, the subject takes precedence, and the login configuration will be + * ignored. + */ + @NonNull + public Builder withLoginConfiguration(@Nullable Map loginConfiguration) { + this.loginConfiguration = fetchLoginConfiguration(loginConfiguration); + return this; + } + + /** + * Sets a previously authenticated subject to reuse. + * + *

You MUST call either this method or {@link #withLoginConfiguration(Configuration)}; if + * both are called, the subject takes precedence, and the login configuration will be ignored. + */ + @NonNull + public Builder withSubject(@Nullable Subject subject) { + this.subject = subject; + return this; + } + + /** + * Sets the SASL protocol name to use; should match the username of the Kerberos service + * principal used by the DSE server. + */ + @NonNull + public Builder withSaslProtocol(@Nullable String saslProtocol) { + this.saslProtocol = saslProtocol; + return this; + } + + /** Sets the authorization ID (allows proxy authentication). */ + @NonNull + public Builder withAuthorizationId(@Nullable String authorizationId) { + this.authorizationId = authorizationId; + return this; + } + + /** + * Add a SASL property to use when creating the SASL client. + * + *

Note that this builder pre-initializes these two default properties: + * + *

+       * javax.security.sasl.server.authentication = true
+       * javax.security.sasl.qop = auth
+       * 
+ */ + @NonNull + public Builder addSaslProperty(@NonNull String name, @NonNull String value) { + this.saslProperties.put(Objects.requireNonNull(name), Objects.requireNonNull(value)); + return this; + } + + @NonNull + public GssApiOptions build() { + return new GssApiOptions( + loginConfiguration, + subject, + saslProtocol, + authorizationId, + ImmutableMap.copyOf(saslProperties)); + } + + public static Configuration fetchLoginConfiguration(Map options) { + return new Configuration() { + + @Override + public AppConfigurationEntry[] getAppConfigurationEntry(String name) { + return new AppConfigurationEntry[] { + new AppConfigurationEntry( + "com.sun.security.auth.module.Krb5LoginModule", + AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, + options) + }; + } + }; + } + } + } + + protected static class GssApiAuthenticator extends BaseDseAuthenticator { + + private static final ByteBuffer MECHANISM = + ByteBuffer.wrap("GSSAPI".getBytes(Charsets.UTF_8)).asReadOnlyBuffer(); + private static final ByteBuffer SERVER_INITIAL_CHALLENGE = + ByteBuffer.wrap("GSSAPI-START".getBytes(Charsets.UTF_8)).asReadOnlyBuffer(); + private static final ByteBuffer EMPTY_BYTE_ARRAY = + ByteBuffer.wrap(new byte[0]).asReadOnlyBuffer(); + private static final String JAAS_CONFIG_ENTRY = "DseClient"; + private static final String[] SUPPORTED_MECHANISMS = new String[] {"GSSAPI"}; + + private Subject subject; + private SaslClient saslClient; + private EndPoint endPoint; + + protected GssApiAuthenticator( + GssApiOptions options, EndPoint endPoint, String serverAuthenticator) { + super(serverAuthenticator); + + try { + if (options.getSubject() != null) { + this.subject = options.getSubject(); + } else { + Configuration loginConfiguration = options.getLoginConfiguration(); + if (loginConfiguration == null) { + throw new IllegalArgumentException("Must provide one of subject or loginConfiguration"); + } + LoginContext login = new LoginContext(JAAS_CONFIG_ENTRY, null, null, loginConfiguration); + login.login(); + this.subject = login.getSubject(); + } + String protocol = options.getSaslProtocol(); + if (protocol == null) { + protocol = + System.getProperty( + SASL_SERVICE_NAME_PROPERTY, + System.getProperty(LEGACY_SASL_PROTOCOL_PROPERTY, DEFAULT_SASL_SERVICE_NAME)); + } + this.saslClient = + Sasl.createSaslClient( + SUPPORTED_MECHANISMS, + options.getAuthorizationId(), + protocol, + ((InetSocketAddress) endPoint.resolve()).getAddress().getCanonicalHostName(), + options.getSaslProperties(), + null); + } catch (LoginException | SaslException e) { + throw new AuthenticationException(endPoint, e.getMessage()); + } + this.endPoint = endPoint; + } + + @NonNull + @Override + protected ByteBuffer getMechanism() { + return MECHANISM; + } + + @NonNull + @Override + protected ByteBuffer getInitialServerChallenge() { + return SERVER_INITIAL_CHALLENGE; + } + + @Nullable + @Override + public ByteBuffer evaluateChallengeSync(@Nullable ByteBuffer challenge) { + + byte[] challengeBytes; + if (SERVER_INITIAL_CHALLENGE.equals(challenge)) { + if (!saslClient.hasInitialResponse()) { + return EMPTY_BYTE_ARRAY; + } + challengeBytes = new byte[0]; + } else { + // The native protocol spec says the incoming challenge can be null depending on the + // implementation. But saslClient.evaluateChallenge clearly documents that the byte array + // can't be null, which probably means that a SASL authenticator never sends back null. + if (challenge == null) { + throw new AuthenticationException(this.endPoint, "Unexpected null challenge from server"); + } + challengeBytes = Bytes.getArray(challenge); + } + try { + + return ByteBuffer.wrap( + Subject.doAs( + subject, + new PrivilegedExceptionAction() { + @Override + public byte[] run() throws SaslException { + return saslClient.evaluateChallenge(challengeBytes); + } + })); + } catch (PrivilegedActionException e) { + throw new AuthenticationException(this.endPoint, e.getMessage(), e.getException()); + } + } + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/auth/DsePlainTextAuthProviderBase.java b/core/src/main/java/com/datastax/dse/driver/api/core/auth/DsePlainTextAuthProviderBase.java new file mode 100644 index 00000000000..7c5ee23bd6c --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/auth/DsePlainTextAuthProviderBase.java @@ -0,0 +1,36 @@ +/* + * 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 com.datastax.dse.driver.api.core.auth; + +import com.datastax.oss.driver.api.core.auth.PlainTextAuthProviderBase; +import edu.umd.cs.findbugs.annotations.NonNull; +import net.jcip.annotations.ThreadSafe; + +/** + * @deprecated The driver's default plain text providers now support both Apache Cassandra and DSE. + * This type was preserved for backward compatibility, but implementors should now extend {@link + * PlainTextAuthProviderBase} instead. + */ +@ThreadSafe +@Deprecated +public abstract class DsePlainTextAuthProviderBase extends PlainTextAuthProviderBase { + + protected DsePlainTextAuthProviderBase(@NonNull String logPrefix) { + super(logPrefix); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/auth/ProgrammaticDseGssApiAuthProvider.java b/core/src/main/java/com/datastax/dse/driver/api/core/auth/ProgrammaticDseGssApiAuthProvider.java new file mode 100644 index 00000000000..64ee5265b5a --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/auth/ProgrammaticDseGssApiAuthProvider.java @@ -0,0 +1,174 @@ +/* + * 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 com.datastax.dse.driver.api.core.auth; + +import com.datastax.oss.driver.api.core.auth.AuthProvider; +import com.datastax.oss.driver.api.core.metadata.EndPoint; +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * {@link AuthProvider} that provides GSSAPI authenticator instances for clients to connect to DSE + * clusters secured with {@code DseAuthenticator}, in a programmatic way. + * + *

To use this provider the corresponding GssApiOptions must be passed into the provider + * directly, for example: + * + *

+ *     DseGssApiAuthProviderBase.GssApiOptions.Builder builder =
+ *         DseGssApiAuthProviderBase.GssApiOptions.builder();
+ *     Map<String, String> loginConfig =
+ *         ImmutableMap.of(
+ *             "principal",
+ *             "user principal here ex cassandra@DATASTAX.COM",
+ *             "useKeyTab",
+ *             "true",
+ *             "refreshKrb5Config",
+ *             "true",
+ *             "keyTab",
+ *             "Path to keytab file here");
+ *
+ *     builder.withLoginConfiguration(loginConfig);
+ *
+ *     CqlSession session =
+ *         CqlSession.builder()
+ *             .withAuthProvider(new ProgrammaticDseGssApiAuthProvider(builder.build()))
+ *             .build();
+ * 
+ * + * or alternatively + * + *
+ *     DseGssApiAuthProviderBase.GssApiOptions.Builder builder =
+ *         DseGssApiAuthProviderBase.GssApiOptions.builder().withSubject(subject);
+ *     CqlSession session =
+ *         CqlSession.builder()
+ *             .withAuthProvider(new ProgrammaticDseGssApiAuthProvider(builder.build()))
+ *             .build();
+ * 
+ * + *

Kerberos Authentication

+ * + * Keytab and ticket cache settings are specified using a standard JAAS configuration file. The + * location of the file can be set using the java.security.auth.login.config system + * property or by adding a login.config.url.n entry in the java.security + * properties file. Alternatively a login-configuration, or subject can be provided to the provider + * via the GssApiOptions (see above). + * + *

See the following documents for further details: + * + *

    + *
  1. JAAS + * Login Configuration File; + *
  2. Krb5LoginModule + * options; + *
  3. JAAS + * Authentication Tutorial for more on JAAS in general. + *
+ * + *

Authentication using ticket cache

+ * + * Run kinit to obtain a ticket and populate the cache before connecting. JAAS config: + * + *
+ * DseClient {
+ *   com.sun.security.auth.module.Krb5LoginModule required
+ *     useTicketCache=true
+ *     renewTGT=true;
+ * };
+ * 
+ * + *

Authentication using a keytab file

+ * + * To enable authentication using a keytab file, specify its location on disk. If your keytab + * contains more than one principal key, you should also specify which one to select. This + * information can also be specified in the driver config, under the login-configuration section. + * + *
+ * DseClient {
+ *     com.sun.security.auth.module.Krb5LoginModule required
+ *       useKeyTab=true
+ *       keyTab="/path/to/file.keytab"
+ *       principal="user@MYDOMAIN.COM";
+ * };
+ * 
+ * + *

Specifying SASL protocol name

+ * + * The SASL protocol name used by this auth provider defaults to " + * {@value #DEFAULT_SASL_SERVICE_NAME}". + * + *

Important: the SASL protocol name should match the username of the Kerberos + * service principal used by the DSE server. This information is specified in the dse.yaml file by + * the {@code service_principal} option under the kerberos_options + * section, and may vary from one DSE installation to another – especially if you installed + * DSE with an automated package installer. + * + *

For example, if your dse.yaml file contains the following: + * + *

{@code
+ * kerberos_options:
+ *     ...
+ *     service_principal: cassandra/my.host.com@MY.REALM.COM
+ * }
+ * + * The correct SASL protocol name to use when authenticating against this DSE server is "{@code + * cassandra}". + * + *

Should you need to change the SASL protocol name specify it in the GssApiOptions, use the + * method below: + * + *

+ *     DseGssApiAuthProviderBase.GssApiOptions.Builder builder =
+ *         DseGssApiAuthProviderBase.GssApiOptions.builder();
+ *     builder.withSaslProtocol("alternate");
+ *     DseGssApiAuthProviderBase.GssApiOptions options = builder.build();
+ * 
+ * + *

Should internal sasl properties need to be set such as qop. This can also be accomplished by + * setting it in the GssApiOptions: + * + *

+ *   DseGssApiAuthProviderBase.GssApiOptions.Builder builder =
+ *         DseGssApiAuthProviderBase.GssApiOptions.builder();
+ *     builder.addSaslProperty("javax.security.sasl.qop", "auth-conf");
+ *     DseGssApiAuthProviderBase.GssApiOptions options = builder.build();
+ * 
+ * + * @see Authenticating + * a DSE cluster with Kerberos + */ +public class ProgrammaticDseGssApiAuthProvider extends DseGssApiAuthProviderBase { + private final GssApiOptions options; + + public ProgrammaticDseGssApiAuthProvider(GssApiOptions options) { + super("Programmatic-Kerberos"); + this.options = options; + } + + @NonNull + @Override + protected GssApiOptions getOptions( + @NonNull EndPoint endPoint, @NonNull String serverAuthenticator) { + return options; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/auth/ProxyAuthentication.java b/core/src/main/java/com/datastax/dse/driver/api/core/auth/ProxyAuthentication.java new file mode 100644 index 00000000000..a3624ba736d --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/auth/ProxyAuthentication.java @@ -0,0 +1,82 @@ +/* + * 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 com.datastax.dse.driver.api.core.auth; + +import com.datastax.dse.driver.api.core.graph.GraphStatement; +import com.datastax.oss.driver.api.core.cql.Statement; +import com.datastax.oss.driver.shaded.guava.common.base.Charsets; +import com.datastax.oss.protocol.internal.util.collection.NullAllowingImmutableMap; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.nio.ByteBuffer; +import java.util.Map; + +public class ProxyAuthentication { + private static final String PROXY_EXECUTE = "ProxyExecute"; + + /** + * Adds proxy authentication information to a CQL statement. + * + *

This allows executing a statement as another role than the one the session is currently + * authenticated as. + * + * @param userOrRole the role to use for execution. If the statement was already configured with + * another role, it will get replaced by this one. + * @param statement the statement to modify. + * @return a statement that will run the same CQL query as {@code statement}, but acting as the + * provided role. Note: with the driver's default implementations, this will always be a copy; + * but if you use a custom implementation, it might return the same instance (depending on the + * behavior of {@link Statement#setCustomPayload(Map) statement.setCustomPayload()}). + * @see Setting + * up roles for applications (DSE 6.0 admin guide) + */ + @NonNull + public static > StatementT executeAs( + @NonNull String userOrRole, @NonNull StatementT statement) { + return statement.setCustomPayload( + addProxyExecuteEntry(statement.getCustomPayload(), userOrRole)); + } + + /** + * Adds proxy authentication information to a graph statement. + * + * @see #executeAs(String, Statement) + */ + @NonNull + public static > StatementT executeAs( + @NonNull String userOrRole, @NonNull StatementT statement) { + return statement.setCustomPayload( + addProxyExecuteEntry(statement.getCustomPayload(), userOrRole)); + } + + private static Map addProxyExecuteEntry( + Map currentPayload, @NonNull String userOrRole) { + NullAllowingImmutableMap.Builder builder = + NullAllowingImmutableMap.builder(); + builder.put(PROXY_EXECUTE, ByteBuffer.wrap(userOrRole.getBytes(Charsets.UTF_8))); + if (!currentPayload.isEmpty()) { + for (Map.Entry entry : currentPayload.entrySet()) { + String key = entry.getKey(); + if (!key.equals(PROXY_EXECUTE)) { + builder.put(key, entry.getValue()); + } + } + } + return builder.build(); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/config/DseDriverConfigLoader.java b/core/src/main/java/com/datastax/dse/driver/api/core/config/DseDriverConfigLoader.java new file mode 100644 index 00000000000..2694b51ffca --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/config/DseDriverConfigLoader.java @@ -0,0 +1,76 @@ +/* + * 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 com.datastax.dse.driver.api.core.config; + +import com.datastax.oss.driver.api.core.config.DriverConfigLoader; +import com.datastax.oss.driver.api.core.config.ProgrammaticDriverConfigLoaderBuilder; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.io.File; +import java.net.URL; + +/** + * @deprecated This class only exists for backward compatibility. All of its methods delegate to + * their counterparts on {@link DriverConfigLoader}, which you should call directly instead. + */ +@Deprecated +public class DseDriverConfigLoader { + + /** + * @deprecated This method only exists for backward compatibility. It delegates to {@link + * DriverConfigLoader#fromClasspath(String)}, which you should call directly instead. + */ + @Deprecated + @NonNull + public static DriverConfigLoader fromClasspath(@NonNull String resourceBaseName) { + return DriverConfigLoader.fromClasspath(resourceBaseName); + } + + /** + * @deprecated This method only exists for backward compatibility. It delegates to {@link + * DriverConfigLoader#fromFile(File)}, which you should call directly instead. + */ + @Deprecated + @NonNull + public static DriverConfigLoader fromFile(@NonNull File file) { + return DriverConfigLoader.fromFile(file); + } + + /** + * @deprecated This method only exists for backward compatibility. It delegates to {@link + * DriverConfigLoader#fromUrl(URL)}, which you should call directly instead. + */ + @Deprecated + @NonNull + public static DriverConfigLoader fromUrl(@NonNull URL url) { + return DriverConfigLoader.fromUrl(url); + } + + /** + * @deprecated This method only exists for backward compatibility. It delegates to {@link + * DriverConfigLoader#programmaticBuilder()}, which you should call directly instead. + */ + @Deprecated + @NonNull + public static ProgrammaticDriverConfigLoaderBuilder programmaticBuilder() { + return DriverConfigLoader.programmaticBuilder(); + } + + private DseDriverConfigLoader() { + throw new AssertionError("Not meant to be instantiated"); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/config/DseDriverOption.java b/core/src/main/java/com/datastax/dse/driver/api/core/config/DseDriverOption.java new file mode 100644 index 00000000000..4d10501f6d2 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/config/DseDriverOption.java @@ -0,0 +1,334 @@ +/* + * 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 com.datastax.dse.driver.api.core.config; + +import com.datastax.oss.driver.api.core.config.DriverOption; +import edu.umd.cs.findbugs.annotations.NonNull; + +public enum DseDriverOption implements DriverOption { + /** + * The name of the application using the session. + * + *

Value type: {@link String} + */ + APPLICATION_NAME("basic.application.name"), + /** + * The version of the application using the session. + * + *

Value type: {@link String} + */ + APPLICATION_VERSION("basic.application.version"), + + /** + * Proxy authentication for GSSAPI authentication: allows to login as another user or role. + * + *

Value type: {@link String} + */ + AUTH_PROVIDER_AUTHORIZATION_ID("advanced.auth-provider.authorization-id"), + /** + * Service name for GSSAPI authentication. + * + *

Value type: {@link String} + */ + AUTH_PROVIDER_SERVICE("advanced.auth-provider.service"), + /** + * Login configuration for GSSAPI authentication. + * + *

Value type: {@link java.util.Map Map}<{@link String},{@link String}> + */ + AUTH_PROVIDER_LOGIN_CONFIGURATION("advanced.auth-provider.login-configuration"), + /** + * Internal SASL properties, if any, such as QOP, for GSSAPI authentication. + * + *

Value type: {@link java.util.Map Map}<{@link String},{@link String}> + */ + AUTH_PROVIDER_SASL_PROPERTIES("advanced.auth-provider.sasl-properties"), + + /** + * The page size for continuous paging. + * + *

Value type: int + */ + CONTINUOUS_PAGING_PAGE_SIZE("advanced.continuous-paging.page-size"), + /** + * Whether {@link #CONTINUOUS_PAGING_PAGE_SIZE} should be interpreted in number of rows or bytes. + * + *

Value type: boolean + */ + CONTINUOUS_PAGING_PAGE_SIZE_BYTES("advanced.continuous-paging.page-size-in-bytes"), + /** + * The maximum number of continuous pages to return. + * + *

Value type: int + */ + CONTINUOUS_PAGING_MAX_PAGES("advanced.continuous-paging.max-pages"), + /** + * The maximum number of continuous pages per second. + * + *

Value type: int + */ + CONTINUOUS_PAGING_MAX_PAGES_PER_SECOND("advanced.continuous-paging.max-pages-per-second"), + /** + * The maximum number of continuous pages that can be stored in the local queue. + * + *

Value type: int + */ + CONTINUOUS_PAGING_MAX_ENQUEUED_PAGES("advanced.continuous-paging.max-enqueued-pages"), + /** + * How long to wait for the coordinator to send the first continuous page. + * + *

Value-type: {@link java.time.Duration Duration} + */ + CONTINUOUS_PAGING_TIMEOUT_FIRST_PAGE("advanced.continuous-paging.timeout.first-page"), + /** + * How long to wait for the coordinator to send subsequent continuous pages. + * + *

Value-type: {@link java.time.Duration Duration} + */ + CONTINUOUS_PAGING_TIMEOUT_OTHER_PAGES("advanced.continuous-paging.timeout.other-pages"), + + /** + * The largest latency that we expect to record for continuous requests. + * + *

Value-type: {@link java.time.Duration Duration} + */ + CONTINUOUS_PAGING_METRICS_SESSION_CQL_REQUESTS_HIGHEST( + "advanced.metrics.session.continuous-cql-requests.highest-latency"), + /** + * The number of significant decimal digits to which internal structures will maintain for + * continuous requests. + * + *

Value-type: int + */ + CONTINUOUS_PAGING_METRICS_SESSION_CQL_REQUESTS_DIGITS( + "advanced.metrics.session.continuous-cql-requests.significant-digits"), + /** + * The interval at which percentile data is refreshed for continuous requests. + * + *

Value-type: {@link java.time.Duration Duration} + */ + CONTINUOUS_PAGING_METRICS_SESSION_CQL_REQUESTS_INTERVAL( + "advanced.metrics.session.continuous-cql-requests.refresh-interval"), + + /** + * The read consistency level to use for graph statements. + * + *

Value type: {@link String} + */ + GRAPH_READ_CONSISTENCY_LEVEL("basic.graph.read-consistency-level"), + /** + * The write consistency level to use for graph statements. + * + *

Value type: {@link String} + */ + GRAPH_WRITE_CONSISTENCY_LEVEL("basic.graph.write-consistency-level"), + /** + * The traversal source to use for graph statements. + * + *

Value type: {@link String} + */ + GRAPH_TRAVERSAL_SOURCE("basic.graph.traversal-source"), + /** + * The sub-protocol the driver will use to communicate with DSE Graph, on top of the Cassandra + * native protocol. + * + *

Value type: {@link String} + */ + GRAPH_SUB_PROTOCOL("advanced.graph.sub-protocol"), + /** + * Whether a script statement represents a system query. + * + *

Value type: boolean + */ + GRAPH_IS_SYSTEM_QUERY("basic.graph.is-system-query"), + /** + * The name of the graph targeted by graph statements. + * + *

Value type: {@link String} + */ + GRAPH_NAME("basic.graph.name"), + /** + * How long the driver waits for a graph request to complete. + * + *

Value-type: {@link java.time.Duration Duration} + */ + GRAPH_TIMEOUT("basic.graph.timeout"), + + /** + * Whether to send events for Insights monitoring. + * + *

Value type: boolean + */ + MONITOR_REPORTING_ENABLED("advanced.monitor-reporting.enabled"), + + /** + * Whether to enable paging for Graph queries. + * + *

Value type: {@link String} + */ + GRAPH_PAGING_ENABLED("advanced.graph.paging-enabled"), + + /** + * The page size for Graph continuous paging. + * + *

Value type: int + */ + GRAPH_CONTINUOUS_PAGING_PAGE_SIZE("advanced.graph.paging-options.page-size"), + + /** + * The maximum number of Graph continuous pages to return. + * + *

Value type: int + */ + GRAPH_CONTINUOUS_PAGING_MAX_PAGES("advanced.graph.paging-options.max-pages"), + /** + * The maximum number of Graph continuous pages per second. + * + *

Value type: int + */ + GRAPH_CONTINUOUS_PAGING_MAX_PAGES_PER_SECOND( + "advanced.graph.paging-options.max-pages-per-second"), + /** + * The maximum number of Graph continuous pages that can be stored in the local queue. + * + *

Value type: int + */ + GRAPH_CONTINUOUS_PAGING_MAX_ENQUEUED_PAGES("advanced.graph.paging-options.max-enqueued-pages"), + /** + * The largest latency that we expect to record for graph requests. + * + *

Value-type: {@link java.time.Duration Duration} + */ + METRICS_SESSION_GRAPH_REQUESTS_HIGHEST("advanced.metrics.session.graph-requests.highest-latency"), + /** + * The number of significant decimal digits to which internal structures will maintain for graph + * requests. + * + *

Value-type: int + */ + METRICS_SESSION_GRAPH_REQUESTS_DIGITS( + "advanced.metrics.session.graph-requests.significant-digits"), + /** + * The interval at which percentile data is refreshed for graph requests. + * + *

Value-type: {@link java.time.Duration Duration} + */ + METRICS_SESSION_GRAPH_REQUESTS_INTERVAL( + "advanced.metrics.session.graph-requests.refresh-interval"), + /** + * The largest latency that we expect to record for graph requests. + * + *

Value-type: {@link java.time.Duration Duration} + */ + METRICS_NODE_GRAPH_MESSAGES_HIGHEST("advanced.metrics.node.graph-messages.highest-latency"), + /** + * The number of significant decimal digits to which internal structures will maintain for graph + * requests. + * + *

Value-type: int + */ + METRICS_NODE_GRAPH_MESSAGES_DIGITS("advanced.metrics.node.graph-messages.significant-digits"), + /** + * The interval at which percentile data is refreshed for graph requests. + * + *

Value-type: {@link java.time.Duration Duration} + */ + METRICS_NODE_GRAPH_MESSAGES_INTERVAL("advanced.metrics.node.graph-messages.refresh-interval"), + + /** + * The shortest latency that we expect to record for continuous requests. + * + *

Value-type: {@link java.time.Duration Duration} + */ + CONTINUOUS_PAGING_METRICS_SESSION_CQL_REQUESTS_LOWEST( + "advanced.metrics.session.continuous-cql-requests.lowest-latency"), + /** + * Optional service-level objectives to meet, as a list of latencies to track. + * + *

Value-type: {@link java.time.Duration Duration} + */ + CONTINUOUS_PAGING_METRICS_SESSION_CQL_REQUESTS_SLO( + "advanced.metrics.session.continuous-cql-requests.slo"), + + /** + * The shortest latency that we expect to record for graph requests. + * + *

Value-type: {@link java.time.Duration Duration} + */ + METRICS_SESSION_GRAPH_REQUESTS_LOWEST("advanced.metrics.session.graph-requests.lowest-latency"), + /** + * Optional service-level objectives to meet, as a list of latencies to track. + * + *

Value-type: {@link java.time.Duration Duration} + */ + METRICS_SESSION_GRAPH_REQUESTS_SLO("advanced.metrics.session.graph-requests.slo"), + + /** + * The shortest latency that we expect to record for graph requests. + * + *

Value-type: {@link java.time.Duration Duration} + */ + METRICS_NODE_GRAPH_MESSAGES_LOWEST("advanced.metrics.node.graph-messages.lowest-latency"), + /** + * Optional service-level objectives to meet, as a list of latencies to track. + * + *

Value-type: {@link java.time.Duration Duration} + */ + METRICS_NODE_GRAPH_MESSAGES_SLO("advanced.metrics.node.graph-messages.slo"), + /** + * Optional list of percentiles to publish for graph-requests metric. Produces an additional time + * series for each requested percentile. This percentile is computed locally, and so can't be + * aggregated with percentiles computed across other dimensions (e.g. in a different instance). + * + *

Value type: {@link java.util.List List}<{@link Double}> + */ + METRICS_SESSION_GRAPH_REQUESTS_PUBLISH_PERCENTILES( + "advanced.metrics.session.graph-requests.publish-percentiles"), + /** + * Optional list of percentiles to publish for node graph-messages metric. Produces an additional + * time series for each requested percentile. This percentile is computed locally, and so can't be + * aggregated with percentiles computed across other dimensions (e.g. in a different instance). + * + *

Value type: {@link java.util.List List}<{@link Double}> + */ + METRICS_NODE_GRAPH_MESSAGES_PUBLISH_PERCENTILES( + "advanced.metrics.node.graph-messages.publish-percentiles"), + /** + * Optional list of percentiles to publish for continuous paging requests metric. Produces an + * additional time series for each requested percentile. This percentile is computed locally, and + * so can't be aggregated with percentiles computed across other dimensions (e.g. in a different + * instance). + * + *

Value type: {@link java.util.List List}<{@link Double}> + */ + CONTINUOUS_PAGING_METRICS_SESSION_CQL_REQUESTS_PUBLISH_PERCENTILES( + "advanced.metrics.session.continuous-cql-requests.publish-percentiles"), + ; + + private final String path; + + DseDriverOption(String path) { + this.path = path; + } + + @NonNull + @Override + public String getPath() { + return path; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/cql/continuous/ContinuousAsyncResultSet.java b/core/src/main/java/com/datastax/dse/driver/api/core/cql/continuous/ContinuousAsyncResultSet.java new file mode 100644 index 00000000000..a9491ec2414 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/cql/continuous/ContinuousAsyncResultSet.java @@ -0,0 +1,79 @@ +/* + * 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 com.datastax.dse.driver.api.core.cql.continuous; + +import com.datastax.oss.driver.api.core.AsyncPagingIterable; +import com.datastax.oss.driver.api.core.cql.ExecutionInfo; +import com.datastax.oss.driver.api.core.cql.Row; +import com.datastax.oss.driver.api.core.cql.Statement; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.nio.ByteBuffer; +import java.util.concurrent.CancellationException; + +/** + * The result of an {@linkplain ContinuousSession#executeContinuouslyAsync(Statement) asynchronous + * continuous paging query}. + * + *

DSE replies to a continuous query with a stream of response frames. There is one instance of + * this class for each frame. + */ +public interface ContinuousAsyncResultSet + extends AsyncPagingIterable { + + /** Returns the current page's number. Pages are numbered starting from 1. */ + int pageNumber(); + + /** + * Cancels the continuous query. + * + *

There might still be rows available in the {@linkplain #currentPage() current page} after + * the cancellation; these rows can be retrieved normally. + * + *

Also, there might be more pages available in the driver's local page cache after the + * cancellation; these extra pages will be discarded. + * + *

Therefore, if you plan to resume the iteration later, the correct procedure is as follows: + * + *

    + *
  1. Cancel the operation by invoking this method, or by cancelling the {@linkplain + * #fetchNextPage() next page's future}; + *
  2. Keep iterating on the current page until it doesn't return any more rows; + *
  3. Retrieve the paging state with {@link #getExecutionInfo() + * getExecutionInfo().getPagingState()}; + *
  4. {@linkplain Statement#setPagingState(ByteBuffer) Re-inject the paging state} in the + * statement; + *
  5. Resume the operation by invoking {@link + * ContinuousSession#executeContinuouslyAsync(Statement) executeContinuouslyAsync} again. + *
+ * + * After a cancellation, futures returned by {@link #fetchNextPage()} that are not yet complete + * will always complete exceptionally by throwing a {@link CancellationException}, even if + * they were obtained before the cancellation. + */ + void cancel(); + + /** + * {@inheritDoc} + * + *

Note: because the driver does not support query traces for continuous queries, {@link + * ExecutionInfo#getTracingId()} will always be {@code null}. + */ + @NonNull + @Override + ExecutionInfo getExecutionInfo(); +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/cql/continuous/ContinuousResultSet.java b/core/src/main/java/com/datastax/dse/driver/api/core/cql/continuous/ContinuousResultSet.java new file mode 100644 index 00000000000..a333801a59a --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/cql/continuous/ContinuousResultSet.java @@ -0,0 +1,80 @@ +/* + * 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 com.datastax.dse.driver.api.core.cql.continuous; + +import com.datastax.oss.driver.api.core.cql.ExecutionInfo; +import com.datastax.oss.driver.api.core.cql.ResultSet; +import com.datastax.oss.driver.api.core.cql.Statement; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.nio.ByteBuffer; +import java.util.List; + +/** + * The result of a {@linkplain ContinuousSession#executeContinuously(Statement) synchronous + * continuous paging query}. + * + *

It uses {@linkplain ContinuousAsyncResultSet asynchronous calls} internally, but blocks on the + * results in order to provide a synchronous API to its clients. If the query is paged, only the + * first page will be fetched initially, and iteration will trigger background fetches of the next + * pages when necessary. + * + *

Note that this object can only be iterated once: rows are "consumed" as they are read, + * subsequent calls to {@code iterator()} will return the same iterator instance. + * + *

Implementations of this type are not thread-safe. They can only be iterated by the + * thread that invoked {@code session.executeContinuously}. + */ +public interface ContinuousResultSet extends ResultSet { + + /** + * Cancels the continuous query. + * + *

There might still be rows available in the current page after the cancellation; the + * iteration will only stop when such rows are fully iterated upon. + * + *

Also, there might be more pages available in the driver's local page cache after the + * cancellation; these extra pages will be discarded. + * + *

Therefore, if you plan to resume the iteration later, the correct procedure is as follows: + * + *

    + *
  1. Cancel the operation by invoking this method; + *
  2. Keep iterating on this object until it doesn't return any more rows; + *
  3. Retrieve the paging state with {@link #getExecutionInfo() + * getExecutionInfo().getPagingState()}; + *
  4. {@linkplain Statement#setPagingState(ByteBuffer) Re-inject the paging state} in the + * statement; + *
  5. Resume the operation by invoking {@link ContinuousSession#executeContinuously(Statement) + * executeContinuously} again. + *
+ */ + void cancel(); + + /** + * {@inheritDoc} + * + *

Note: because the driver does not support query traces for continuous queries, {@link + * ExecutionInfo#getTracingId()} will always be {@code null}. + */ + @NonNull + @Override + default ExecutionInfo getExecutionInfo() { + List infos = getExecutionInfos(); + return infos.get(infos.size() - 1); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/cql/continuous/ContinuousSession.java b/core/src/main/java/com/datastax/dse/driver/api/core/cql/continuous/ContinuousSession.java new file mode 100644 index 00000000000..1c647b33b92 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/cql/continuous/ContinuousSession.java @@ -0,0 +1,116 @@ +/* + * 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 com.datastax.dse.driver.api.core.cql.continuous; + +import com.datastax.dse.driver.internal.core.cql.continuous.ContinuousCqlRequestAsyncProcessor; +import com.datastax.dse.driver.internal.core.cql.continuous.ContinuousCqlRequestSyncProcessor; +import com.datastax.oss.driver.api.core.DefaultConsistencyLevel; +import com.datastax.oss.driver.api.core.cql.Statement; +import com.datastax.oss.driver.api.core.loadbalancing.LoadBalancingPolicy; +import com.datastax.oss.driver.api.core.metadata.token.Token; +import com.datastax.oss.driver.api.core.session.Session; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.nio.ByteBuffer; +import java.util.Objects; +import java.util.concurrent.CompletionStage; + +/** + * A session that has the ability to execute continuous paging queries. + * + *

Continuous paging is a new method of streaming bulk amounts of records from DataStax + * Enterprise (DSE) to the Java Driver, available since DSE 5.1. It is mainly intended to be + * leveraged by DSE + * Analytics and Apache Spark™, or by any similar analytics tool that needs to read large + * portions of a table in one single operation, as quick and reliably as possible. + * + *

Continuous paging provides the best performance improvement against regular paging when the + * following conditions are met: + * + *

    + *
  1. The statement must target a single partition or a token range owned by one single replica; + * in practice, this means that the statement must have either a {@linkplain + * Statement#setRoutingKey(ByteBuffer) routing key} or a {@linkplain + * Statement#setRoutingToken(Token) routing token} set; + *
  2. The coordinator must be a replica; in practice, this is usually achieved by using + * token-aware routing (if you are using the driver's default {@link LoadBalancingPolicy}, + * then this condition is met); + *
  3. The consistency level must be {@link DefaultConsistencyLevel#ONE ONE} (or {@link + * DefaultConsistencyLevel#LOCAL_ONE LOCAL_ONE}). + *
+ * + *

If the above conditions are met, the coordinator will be able to optimize the read path and + * serve results from local data, thus significantly improving response times; if however these + * conditions cannot be met, continuous paging would still work, but response times wouldn't be + * significantly better than those of regular paging anymore. + * + * @see Continuous + * paging options in cassandra.yaml configuration file + * @see DSE + * Continuous Paging Tuning and Support Guide + */ +public interface ContinuousSession extends Session { + + /** + * Executes the provided query with continuous paging synchronously. + * + *

This method takes care of chaining the successive results into a convenient iterable, + * provided that you always access the result from the same thread. For more flexibility, consider + * using the {@linkplain #executeContinuouslyAsync(Statement) asynchronous variant} of this method + * instead. + * + *

See {@link ContinuousSession} for more explanations about continuous paging. + * + *

This feature is only available with DataStax Enterprise. Executing continuous queries + * against an Apache Cassandra© cluster will result in a runtime error. + * + * @param statement the query to execute. + * @return a synchronous iterable on the results. + */ + @NonNull + default ContinuousResultSet executeContinuously(@NonNull Statement statement) { + return Objects.requireNonNull( + execute(statement, ContinuousCqlRequestSyncProcessor.CONTINUOUS_RESULT_SYNC)); + } + + /** + * Executes the provided query with continuous paging asynchronously. + * + *

The server will push all requested pages asynchronously, according to the options defined in + * the current execution profile. The client should consume all pages as quickly as possible, to + * avoid blocking the server for too long. The server will adjust the rate according to the client + * speed, but it will give up if the client does not consume any pages in a period of time equal + * to the read request timeout. + * + *

See {@link ContinuousSession} for more explanations about continuous paging. + * + *

This feature is only available with DataStax Enterprise. Executing continuous queries + * against an Apache Cassandra© cluster will result in a runtime error. + * + * @param statement the query to execute. + * @return a future to the first asynchronous result. + */ + @NonNull + default CompletionStage executeContinuouslyAsync( + @NonNull Statement statement) { + return Objects.requireNonNull( + execute(statement, ContinuousCqlRequestAsyncProcessor.CONTINUOUS_RESULT_ASYNC)); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/cql/continuous/reactive/ContinuousReactiveResultSet.java b/core/src/main/java/com/datastax/dse/driver/api/core/cql/continuous/reactive/ContinuousReactiveResultSet.java new file mode 100644 index 00000000000..6b645ad05bf --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/cql/continuous/reactive/ContinuousReactiveResultSet.java @@ -0,0 +1,29 @@ +/* + * 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 com.datastax.dse.driver.api.core.cql.continuous.reactive; + +import com.datastax.dse.driver.api.core.cql.reactive.ReactiveResultSet; +import com.datastax.oss.driver.api.core.cql.Statement; + +/** + * A marker interface for publishers returned by {@link ContinuousReactiveSession}. + * + * @see ContinuousReactiveSession#executeContinuouslyReactive(String) + * @see ContinuousReactiveSession#executeContinuouslyReactive(Statement) + */ +public interface ContinuousReactiveResultSet extends ReactiveResultSet {} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/cql/continuous/reactive/ContinuousReactiveSession.java b/core/src/main/java/com/datastax/dse/driver/api/core/cql/continuous/reactive/ContinuousReactiveSession.java new file mode 100644 index 00000000000..d00013731cb --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/cql/continuous/reactive/ContinuousReactiveSession.java @@ -0,0 +1,84 @@ +/* + * 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 com.datastax.dse.driver.api.core.cql.continuous.reactive; + +import com.datastax.dse.driver.api.core.cql.continuous.ContinuousSession; +import com.datastax.dse.driver.api.core.cql.reactive.ReactiveRow; +import com.datastax.dse.driver.internal.core.cql.continuous.reactive.ContinuousCqlRequestReactiveProcessor; +import com.datastax.oss.driver.api.core.cql.SimpleStatement; +import com.datastax.oss.driver.api.core.cql.Statement; +import com.datastax.oss.driver.api.core.session.Session; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.Objects; +import org.reactivestreams.Publisher; + +/** + * A {@link Session} that offers utility methods to issue queries using reactive-style programming + * and continuous paging, combined together. + * + *

Methods in this interface all return {@link ContinuousReactiveResultSet} instances. All + * publishers support multiple subscriptions in a unicast fashion: each subscriber triggers an + * independent request execution and gets its own copy of the results. + * + *

Also, note that the publishers may emit items to their subscribers on an internal driver IO + * thread. Subscriber implementors are encouraged to abide by Reactive Streams + * Specification rule 2.2 and avoid performing heavy computations or blocking calls inside + * {@link org.reactivestreams.Subscriber#onNext(Object) onNext} calls, as doing so could slow down + * the driver and impact performance. Instead, they should asynchronously dispatch received signals + * to their processing logic. + * + * @see ReactiveRow + */ +public interface ContinuousReactiveSession extends Session { + + /** + * Returns a {@link Publisher} that, once subscribed to, executes the given query continuously and + * emits all the results. + * + *

See {@link ContinuousSession} for more explanations about continuous paging. + * + *

This feature is only available with DataStax Enterprise. Executing continuous queries + * against an Apache Cassandra® cluster will result in a runtime error. + * + * @param query the query to execute. + * @return The {@link Publisher} that will publish the returned results. + */ + @NonNull + default ContinuousReactiveResultSet executeContinuouslyReactive(@NonNull String query) { + return executeContinuouslyReactive(SimpleStatement.newInstance(query)); + } + + /** + * Returns a {@link Publisher} that, once subscribed to, executes the given query continuously and + * emits all the results. + * + *

See {@link ContinuousSession} for more explanations about continuous paging. + * + *

This feature is only available with DataStax Enterprise. Executing continuous queries + * against an Apache Cassandra® cluster will result in a runtime error. + * + * @param statement the statement to execute. + * @return The {@link Publisher} that will publish the returned results. + */ + @NonNull + default ContinuousReactiveResultSet executeContinuouslyReactive(@NonNull Statement statement) { + return Objects.requireNonNull( + execute(statement, ContinuousCqlRequestReactiveProcessor.CONTINUOUS_REACTIVE_RESULT_SET)); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/cql/reactive/ReactiveQueryMetadata.java b/core/src/main/java/com/datastax/dse/driver/api/core/cql/reactive/ReactiveQueryMetadata.java new file mode 100644 index 00000000000..55a898cd3ee --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/cql/reactive/ReactiveQueryMetadata.java @@ -0,0 +1,88 @@ +/* + * 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 com.datastax.dse.driver.api.core.cql.reactive; + +import com.datastax.oss.driver.api.core.cql.ColumnDefinitions; +import com.datastax.oss.driver.api.core.cql.ExecutionInfo; +import edu.umd.cs.findbugs.annotations.NonNull; +import org.reactivestreams.Publisher; + +/** + * Interface implemented by all the reactive result set publishers provided by the driver, and + * notably by {@link ReactiveResultSet}. + */ +public interface ReactiveQueryMetadata { + + /** + * Returns metadata about the {@linkplain ColumnDefinitions columns} contained in this result set. + * + *

This publisher emits exactly one item as soon as the first response arrives, then completes. + * If the query execution fails within the first request-response cycle, then this + * publisher will fail with the same error; however if the error happens after the first + * response, then this publisher will be already completed and will not acknowledge that + * error in any way. + * + *

By default, publishers returned by this method do not support multiple subscriptions. + * + * @see ReactiveRow#getColumnDefinitions() + */ + @NonNull + Publisher getColumnDefinitions(); + + /** + * Returns {@linkplain ExecutionInfo information about the execution} of all requests that have + * been performed so far to assemble this result set. + * + *

If the query is not paged, this publisher will emit exactly one item as soon as the response + * arrives, then complete. If the query is paged, it will emit multiple items, one per page; then + * it will complete when the last page arrives. If the query execution fails, then this publisher + * will fail with the same error. + * + *

By default, publishers returned by this method do not support multiple subscriptions. + * + * @see ReactiveRow#getExecutionInfo() + */ + @NonNull + Publisher getExecutionInfos(); + + /** + * If the query that produced this result was a conditional update, indicates whether it was + * successfully applied. + * + *

This publisher emits exactly one item as soon as the first response arrives, then completes. + * If the query execution fails within the first request-response cycle, then this + * publisher will fail with the same error; however if the error happens after the first + * response, then this publisher will be already completed and will not acknowledge that + * error in any way. + * + *

By default, publishers returned by this method do not support multiple subscriptions. + * + *

For consistency, this method always returns {@code true} for non-conditional queries + * (although there is no reason to call the method in that case). This is also the case for + * conditional DDL statements ({@code CREATE KEYSPACE... IF NOT EXISTS}, {@code CREATE TABLE... IF + * NOT EXISTS}), for which Cassandra doesn't return an {@code [applied]} column. + * + *

Note that, for versions of Cassandra strictly lower than 2.1.0-rc2, a server-side bug (CASSANDRA-7337) causes this + * method to always return {@code true} for batches containing conditional queries. + * + * @see ReactiveRow#wasApplied() + */ + @NonNull + Publisher wasApplied(); +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/cql/reactive/ReactiveResultSet.java b/core/src/main/java/com/datastax/dse/driver/api/core/cql/reactive/ReactiveResultSet.java new file mode 100644 index 00000000000..0e44dab8cab --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/cql/reactive/ReactiveResultSet.java @@ -0,0 +1,49 @@ +/* + * 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 com.datastax.dse.driver.api.core.cql.reactive; + +import com.datastax.oss.driver.api.core.cql.Statement; +import org.reactivestreams.Publisher; + +/** + * A {@link Publisher} of {@link ReactiveRow}s returned by a {@link ReactiveSession}. + * + *

By default, all implementations returned by the driver are cold, unicast, single-subscriber + * only publishers. In other words, they do not support multiple subscriptions; consider + * caching the results produced by such publishers if you need to consume them by more than one + * downstream subscriber. + * + *

Also, note that reactive result sets may emit items to their subscribers on an internal driver + * IO thread. Subscriber implementors are encouraged to abide by Reactive Streams + * Specification rule 2.2 and avoid performing heavy computations or blocking calls inside + * {@link org.reactivestreams.Subscriber#onNext(Object) onNext} calls, as doing so could slow down + * the driver and impact performance. Instead, they should asynchronously dispatch received signals + * to their processing logic. + * + *

This interface exists mainly to expose useful information about {@linkplain + * #getExecutionInfos() request execution} and {@linkplain #getColumnDefinitions() query metadata}. + * This is particularly convenient for queries that do not return rows; for queries that do return + * rows, it is also possible, and oftentimes easier, to access that same information {@linkplain + * ReactiveRow at row level}. + * + * @see ReactiveSession#executeReactive(String) + * @see ReactiveSession#executeReactive(Statement) + * @see ReactiveRow + */ +public interface ReactiveResultSet extends Publisher, ReactiveQueryMetadata {} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/cql/reactive/ReactiveRow.java b/core/src/main/java/com/datastax/dse/driver/api/core/cql/reactive/ReactiveRow.java new file mode 100644 index 00000000000..c3b94689580 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/cql/reactive/ReactiveRow.java @@ -0,0 +1,93 @@ +/* + * 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 com.datastax.dse.driver.api.core.cql.reactive; + +import com.datastax.oss.driver.api.core.cql.ColumnDefinitions; +import com.datastax.oss.driver.api.core.cql.ExecutionInfo; +import com.datastax.oss.driver.api.core.cql.Row; +import com.datastax.oss.driver.api.core.type.DataTypes; +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * A row produced by a {@linkplain ReactiveResultSet reactive result set}. + * + *

This is essentially an extension of the driver's {@link Row} object that also exposes useful + * information about {@linkplain #getExecutionInfo() request execution} and {@linkplain + * #getColumnDefinitions() query metadata} (note however that this information is also exposed at + * result set level for convenience). + * + * @see ReactiveSession + * @see ReactiveResultSet + */ +public interface ReactiveRow extends Row { + + /** + * Returns the column definitions contained in this row. + * + *

This object is the same for all rows pertaining to the same result set. + * + * @return the column definitions contained in this row. + * @see ReactiveResultSet#getColumnDefinitions() + */ + @NonNull + @Override + ColumnDefinitions getColumnDefinitions(); + + /** + * The execution information for the paged request that produced this result. + * + *

This object is the same for two rows pertaining to the same page, but differs for rows + * pertaining to different pages. + * + * @return the execution information for the paged request that produced this result. + * @see ReactiveResultSet#getExecutionInfos() + */ + @NonNull + ExecutionInfo getExecutionInfo(); + + /** + * If the query that produced this result was a conditional update, indicates whether it was + * successfully applied. + * + *

This is equivalent to calling: + * + *

{@code
+   * ReactiveRow row = ...
+   * boolean wasApplied = row.getBoolean("[applied]");
+   * }
+ * + *

For consistency, this method always returns {@code true} for non-conditional queries + * (although there is no reason to call the method in that case). This is also the case for + * conditional DDL statements ({@code CREATE KEYSPACE... IF NOT EXISTS}, {@code CREATE TABLE... IF + * NOT EXISTS}), for which Cassandra doesn't return an {@code [applied]} column. + * + *

Note that, for versions of Cassandra strictly lower than 2.1.0-rc2, a server-side bug (CASSANDRA-7337) causes this + * method to always return {@code true} for batches containing conditional queries. + * + *

This method always return the same value for all results in the result set. + * + * @return {@code true} for non-conditional queries and for conditional queries that were + * successfully applied, {@code false} otherwise. + */ + default boolean wasApplied() { + return !getColumnDefinitions().contains("[applied]") + || !getColumnDefinitions().get("[applied]").getType().equals(DataTypes.BOOLEAN) + || getBoolean("[applied]"); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/cql/reactive/ReactiveSession.java b/core/src/main/java/com/datastax/dse/driver/api/core/cql/reactive/ReactiveSession.java new file mode 100644 index 00000000000..2fd8ffe41c2 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/cql/reactive/ReactiveSession.java @@ -0,0 +1,106 @@ +/* + * 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 com.datastax.dse.driver.api.core.cql.reactive; + +import com.datastax.dse.driver.internal.core.cql.reactive.CqlRequestReactiveProcessor; +import com.datastax.oss.driver.api.core.cql.SimpleStatement; +import com.datastax.oss.driver.api.core.cql.Statement; +import com.datastax.oss.driver.api.core.session.Session; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.Map; +import java.util.Objects; +import org.reactivestreams.Publisher; + +/** + * A {@link Session} that offers utility methods to issue queries using reactive-style programming. + * + *

Methods in this interface all return {@link ReactiveResultSet} instances. See the javadocs of + * this interface for important remarks anc caveats regarding the subscription to and consumption of + * reactive result sets. + * + * @see ReactiveResultSet + * @see ReactiveRow + */ +public interface ReactiveSession extends Session { + + /** + * Returns a {@link Publisher} that, once subscribed to, executes the given query and emits all + * the results. + * + *

This is an alias for {@link #executeReactive(Statement)} + * executeReactive(SimpleStatement.newInstance(query))}. + * + * @param query the query to execute. + * @return The {@link Publisher} that will publish the returned results. + * @see SimpleStatement#newInstance(String) + */ + @NonNull + default ReactiveResultSet executeReactive(@NonNull String query) { + return executeReactive(SimpleStatement.newInstance(query)); + } + + /** + * Returns a {@link Publisher} that, once subscribed to, executes the given query and emits all + * the results. + * + *

This is an alias for {@link #executeReactive(Statement)} + * executeReactive(SimpleStatement.newInstance(query, values))}. + * + * @param query the query to execute. + * @param values the values for placeholders in the query string. Individual values can be {@code + * null}, but the vararg array itself can't. + * @return The {@link Publisher} that will publish the returned results. + * @see SimpleStatement#newInstance(String,Object...) + */ + @NonNull + default ReactiveResultSet executeReactive(@NonNull String query, @NonNull Object... values) { + return executeReactive(SimpleStatement.newInstance(query, values)); + } + + /** + * Returns a {@link Publisher} that, once subscribed to, executes the given query and emits all + * the results. + * + *

This is an alias for {@link #executeReactive(Statement)} + * executeReactive(SimpleStatement.newInstance(query,values))}. + * + * @param query the query to execute. + * @param values the values for named placeholders in the query string. Individual values can be + * {@code null}, but the map itself can't. + * @return The {@link Publisher} that will publish the returned results. + * @see SimpleStatement#newInstance(String,Map) + */ + @NonNull + default ReactiveResultSet executeReactive( + @NonNull String query, @NonNull Map values) { + return executeReactive(SimpleStatement.newInstance(query, values)); + } + + /** + * Returns a {@link Publisher} that, once subscribed to, executes the given query and emits all + * the results. + * + * @param statement the statement to execute. + * @return The {@link Publisher} that will publish the returned results. + */ + @NonNull + default ReactiveResultSet executeReactive(@NonNull Statement statement) { + return Objects.requireNonNull( + execute(statement, CqlRequestReactiveProcessor.REACTIVE_RESULT_SET)); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/cql/reactive/package-info.java b/core/src/main/java/com/datastax/dse/driver/api/core/cql/reactive/package-info.java new file mode 100644 index 00000000000..01a5f514aba --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/cql/reactive/package-info.java @@ -0,0 +1,24 @@ +/* + * 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. + */ +/** + * Types related to CQL execution using reactive-style programming. + * + *

Note that this is located in a {@code dse} package for historical reasons; reactive queries + * can now be used with open-source Cassandra as well. + */ +package com.datastax.dse.driver.api.core.cql.reactive; diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/data/geometry/Geometry.java b/core/src/main/java/com/datastax/dse/driver/api/core/data/geometry/Geometry.java new file mode 100644 index 00000000000..66a5708832e --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/data/geometry/Geometry.java @@ -0,0 +1,65 @@ +/* + * 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 com.datastax.dse.driver.api.core.data.geometry; + +import edu.umd.cs.findbugs.annotations.NonNull; +import java.nio.ByteBuffer; + +/** + * The driver-side representation for a DSE geospatial type. + * + *

+ *     Row row = dseSession.execute("SELECT coords FROM points_of_interest WHERE name = 'Eiffel Tower'").one();
+ *     Point coords = row.get("coords", Point.class);
+ * 
+ * + * The default implementations returned by the driver are immutable and serializable. If you write + * your own implementations, they should at least be thread-safe; serializability is not mandatory, + * but recommended for use with some 3rd-party tools like Apache Spark ™. + */ +public interface Geometry { + + /** + * Returns a Well-known Text (WKT) + * representation of this geospatial type. + */ + @NonNull + String asWellKnownText(); + + /** + * Returns a Well-known + * Binary (WKB) representation of this geospatial type. + * + *

Note that, due to DSE implementation details, the resulting byte buffer always uses + * little-endian order, regardless of the platform's native order. + */ + @NonNull + ByteBuffer asWellKnownBinary(); + + /** Returns a JSON representation of this geospatial type. */ + @NonNull + String asGeoJson(); + + /** + * Tests whether this geospatial type instance contains another instance. + * + * @param other the other instance. + * @return whether {@code this} contains {@code other}. + */ + boolean contains(@NonNull Geometry other); +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/data/geometry/LineString.java b/core/src/main/java/com/datastax/dse/driver/api/core/data/geometry/LineString.java new file mode 100644 index 00000000000..7f77b3202a2 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/data/geometry/LineString.java @@ -0,0 +1,91 @@ +/* + * 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 com.datastax.dse.driver.api.core.data.geometry; + +import com.datastax.dse.driver.internal.core.data.geometry.DefaultGeometry; +import com.datastax.dse.driver.internal.core.data.geometry.DefaultLineString; +import com.esri.core.geometry.ogc.OGCLineString; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.nio.ByteBuffer; +import java.util.List; + +/** + * The driver-side representation for DSE's {@code LineString}. + * + *

This is a curve in a two-dimensional XY-plane, represented by a set of points (with linear + * interpolation between them). + * + *

The default implementation returned by the driver is immutable. + */ +public interface LineString extends Geometry { + /** + * Creates a line string from its Well-known Text (WKT) representation. + * + * @param source the Well-known Text representation to parse. + * @return the line string represented by the WKT. + * @throws IllegalArgumentException if the string does not contain a valid Well-known Text + * representation. + */ + @NonNull + static LineString fromWellKnownText(@NonNull String source) { + return new DefaultLineString(DefaultGeometry.fromOgcWellKnownText(source, OGCLineString.class)); + } + + /** + * Creates a line string from its Well-known Binary + * (WKB) representation. + * + * @param source the Well-known Binary representation to parse. + * @return the line string represented by the WKB. + * @throws IllegalArgumentException if the provided {@link ByteBuffer} does not contain a valid + * Well-known Binary representation. + */ + @NonNull + static LineString fromWellKnownBinary(@NonNull ByteBuffer source) { + return new DefaultLineString( + DefaultGeometry.fromOgcWellKnownBinary(source, OGCLineString.class)); + } + + /** + * Creates a line string from a GeoJSON + * LineString representation. + * + * @param source the GeoJSON + * LineString representation to parse. + * @return the line string represented by the GeoJSON LineString. + * @throws IllegalArgumentException if the string does not contain a valid GeoJSON LineString + * representation. + */ + @NonNull + static LineString fromGeoJson(@NonNull String source) { + return new DefaultLineString(DefaultGeometry.fromOgcGeoJson(source, OGCLineString.class)); + } + + /** Creates a line string from two or more points. */ + @NonNull + static LineString fromPoints(@NonNull Point p1, @NonNull Point p2, @NonNull Point... pn) { + return new DefaultLineString(p1, p2, pn); + } + + @NonNull + List getPoints(); +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/data/geometry/Point.java b/core/src/main/java/com/datastax/dse/driver/api/core/data/geometry/Point.java new file mode 100644 index 00000000000..b064b3fb222 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/data/geometry/Point.java @@ -0,0 +1,103 @@ +/* + * 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 com.datastax.dse.driver.api.core.data.geometry; + +import com.datastax.dse.driver.internal.core.data.geometry.DefaultGeometry; +import com.datastax.dse.driver.internal.core.data.geometry.DefaultPoint; +import com.esri.core.geometry.ogc.OGCPoint; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.nio.ByteBuffer; + +/** + * The driver-side representation of DSE's {@code Point}. + * + *

This is a zero-dimensional object that represents a specific (X,Y) location in a + * two-dimensional XY-plane. In case of Geographic Coordinate Systems, the X coordinate is the + * longitude and the Y is the latitude. + * + *

The default implementation returned by the driver is immutable. + */ +public interface Point extends Geometry { + + /** + * Creates a point from its Well-known + * Text (WKT) representation. + * + * @param source the Well-known Text representation to parse. + * @return the point represented by the WKT. + * @throws IllegalArgumentException if the string does not contain a valid Well-known Text + * representation. + */ + @NonNull + static Point fromWellKnownText(@NonNull String source) { + return new DefaultPoint(DefaultGeometry.fromOgcWellKnownText(source, OGCPoint.class)); + } + + /** + * Creates a point from its Well-known Binary + * (WKB) representation. + * + * @param source the Well-known Binary representation to parse. + * @return the point represented by the WKB. + * @throws IllegalArgumentException if the provided {@link ByteBuffer} does not contain a valid + * Well-known Binary representation. + */ + @NonNull + static Point fromWellKnownBinary(@NonNull ByteBuffer source) { + return new DefaultPoint(DefaultGeometry.fromOgcWellKnownBinary(source, OGCPoint.class)); + } + + /** + * Creates a point from a GeoJSON + * Point representation. + * + * @param source the GeoJSON Point + * representation to parse. + * @return the point represented by the GeoJSON Point. + * @throws IllegalArgumentException if the string does not contain a valid GeoJSON Point representation. + */ + @NonNull + static Point fromGeoJson(@NonNull String source) { + return new DefaultPoint(DefaultGeometry.fromOgcGeoJson(source, OGCPoint.class)); + } + + /** + * Creates a new point. + * + * @param x The X coordinate of this point (or its longitude in Geographic Coordinate Systems). + * @param y The Y coordinate of this point (or its latitude in Geographic Coordinate Systems). + * @return the point represented by coordinates. + */ + @NonNull + static Point fromCoordinates(double x, double y) { + return new DefaultPoint(x, y); + } + + /** + * Returns the X coordinate of this 2D point (or its longitude in Geographic Coordinate Systems). + */ + double X(); + + /** + * Returns the Y coordinate of this 2D point (or its latitude in Geographic Coordinate Systems). + */ + double Y(); +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/data/geometry/Polygon.java b/core/src/main/java/com/datastax/dse/driver/api/core/data/geometry/Polygon.java new file mode 100644 index 00000000000..d793704defa --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/data/geometry/Polygon.java @@ -0,0 +1,127 @@ +/* + * 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 com.datastax.dse.driver.api.core.data.geometry; + +import com.datastax.dse.driver.internal.core.data.geometry.DefaultGeometry; +import com.datastax.dse.driver.internal.core.data.geometry.DefaultPolygon; +import com.esri.core.geometry.ogc.OGCPolygon; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.nio.ByteBuffer; +import java.util.List; + +/** + * The driver-side representation of DSE's {@code Polygon}. + * + *

This is a planar surface in a two-dimensional XY-plane, represented by one exterior boundary + * and 0 or more interior boundaries. + * + *

The default implementation returned by the driver is immutable. + */ +public interface Polygon extends Geometry { + /** + * Creates a polygon from its Well-known + * Text (WKT) representation. + * + * @param source the Well-known Text representation to parse. + * @return the polygon represented by the WKT. + * @throws IllegalArgumentException if the string does not contain a valid Well-known Text + * representation. + */ + @NonNull + static Polygon fromWellKnownText(@NonNull String source) { + return new DefaultPolygon(DefaultGeometry.fromOgcWellKnownText(source, OGCPolygon.class)); + } + + /** + * Creates a polygon from its Well-known Binary + * (WKB) representation. + * + * @param source the Well-known Binary representation to parse. + * @return the polygon represented by the WKB. + * @throws IllegalArgumentException if the provided {@link ByteBuffer} does not contain a valid + * Well-known Binary representation. + */ + @NonNull + static Polygon fromWellKnownBinary(@NonNull ByteBuffer source) { + return new DefaultPolygon(DefaultGeometry.fromOgcWellKnownBinary(source, OGCPolygon.class)); + } + + /** + * Creates a polygon from a GeoJSON + * Polygon representation. + * + * @param source the GeoJSON Polygon + * representation to parse. + * @return the polygon represented by the GeoJSON Polygon. + * @throws IllegalArgumentException if the string does not contain a valid GeoJSON Polygon representation. + */ + @NonNull + static Polygon fromGeoJson(@NonNull String source) { + return new DefaultPolygon(DefaultGeometry.fromOgcGeoJson(source, OGCPolygon.class)); + } + + /** Creates a polygon from a series of 3 or more points. */ + @NonNull + static Polygon fromPoints( + @NonNull Point p1, @NonNull Point p2, @NonNull Point p3, @NonNull Point... pn) { + return new DefaultPolygon(p1, p2, p3, pn); + } + + /** + * Returns a polygon builder. + * + *

This is intended for complex polygons with multiple rings (i.e. holes inside the polygon). + * For simple cases, consider {@link #fromPoints(Point, Point, Point, Point...)} instead. + */ + @NonNull + static Builder builder() { + return new DefaultPolygon.Builder(); + } + + /** Returns the external ring of the polygon. */ + @NonNull + List getExteriorRing(); + + /** + * Returns the internal rings of the polygon, i.e. any holes inside of it (or islands inside of + * the holes). + */ + @NonNull + List> getInteriorRings(); + + /** Provides a simple DSL to build a polygon. */ + interface Builder { + /** + * Adds a new ring for this polygon. + * + *

There can be one or more outer rings and zero or more inner rings. If a polygon has an + * inner ring, the inner ring looks like a hole. If the hole contains another outer ring, that + * outer ring looks like an island. + * + *

There must be one "main" outer ring that contains all the others. + */ + @NonNull + Builder addRing(@NonNull Point p1, @NonNull Point p2, @NonNull Point p3, @NonNull Point... pn); + + @NonNull + Polygon build(); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/data/time/DateRange.java b/core/src/main/java/com/datastax/dse/driver/api/core/data/time/DateRange.java new file mode 100644 index 00000000000..3dd48915dba --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/data/time/DateRange.java @@ -0,0 +1,257 @@ +/* + * 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 com.datastax.dse.driver.api.core.data.time; + +import com.datastax.oss.driver.shaded.guava.common.base.Preconditions; +import com.datastax.oss.driver.shaded.guava.common.base.Strings; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.io.Serializable; +import java.text.ParseException; +import java.time.ZonedDateTime; +import java.util.Objects; +import java.util.Optional; + +/** + * A date range, as defined by the server type {@code + * org.apache.cassandra.db.marshal.DateRangeType}, corresponding to the Apache Solr type {@code + * DateRangeField}. + * + *

A date range can be either {@linkplain DateRange#DateRange(DateRangeBound) single-bounded}, in + * which case it represents a unique instant (e.g. "{@code 2001-01-01}"), or {@linkplain + * #DateRange(DateRangeBound, DateRangeBound) double-bounded}, in which case it represents an + * interval of time (e.g. "{@code [2001-01-01 TO 2002]}"). + * + *

Date range {@linkplain DateRangeBound bounds} are always inclusive; they must be either valid + * dates, or the special value {@link DateRangeBound#UNBOUNDED UNBOUNDED}, represented by a "{@code + * *}", e.g. "{@code [2001 TO *]}". + * + *

Instances can be more easily created with the {@link #parse(String)} method. + * + *

This class is immutable and thread-safe. + * + * @since DSE 5.1 + */ +public class DateRange implements Serializable { + + /** + * Parses the given string as a date range. + * + *

The given input must be compliant with Apache Solr type {@code + * DateRangeField} syntax; it can either be a {@linkplain #DateRange(DateRangeBound) + * single-bounded range}, or a {@linkplain #DateRange(DateRangeBound, DateRangeBound) + * double-bounded range}. + * + * @throws ParseException if the given string could not be parsed into a valid range. + * @see DateRangeBound#parseLowerBound(String) + * @see DateRangeBound#parseUpperBound(String) + */ + @NonNull + public static DateRange parse(@NonNull String source) throws ParseException { + if (Strings.isNullOrEmpty(source)) { + throw new ParseException("Date range is null or empty", 0); + } + + if (source.charAt(0) == '[') { + if (source.charAt(source.length() - 1) != ']') { + throw new ParseException( + "If date range starts with '[' it must end with ']'; got " + source, + source.length() - 1); + } + int middle = source.indexOf(" TO "); + if (middle < 0) { + throw new ParseException( + "If date range starts with '[' it must contain ' TO '; got " + source, 0); + } + String lowerBoundString = source.substring(1, middle); + int upperBoundStart = middle + 4; + String upperBoundString = source.substring(upperBoundStart, source.length() - 1); + DateRangeBound lowerBound; + try { + lowerBound = DateRangeBound.parseLowerBound(lowerBoundString); + } catch (Exception e) { + throw newParseException("Cannot parse date range lower bound: " + source, 1, e); + } + DateRangeBound upperBound; + try { + upperBound = DateRangeBound.parseUpperBound(upperBoundString); + } catch (Exception e) { + throw newParseException( + "Cannot parse date range upper bound: " + source, upperBoundStart, e); + } + return new DateRange(lowerBound, upperBound); + } else { + try { + return new DateRange(DateRangeBound.parseLowerBound(source)); + } catch (Exception e) { + throw newParseException("Cannot parse single date range bound: " + source, 0, e); + } + } + } + + @NonNull private final DateRangeBound lowerBound; + @Nullable private final DateRangeBound upperBound; + + /** + * Creates a "single bounded" instance, i.e., a date range whose upper and lower bounds are + * identical. + * + * @throws NullPointerException if {@code singleBound} is null. + */ + public DateRange(@NonNull DateRangeBound singleBound) { + this.lowerBound = Preconditions.checkNotNull(singleBound, "singleBound cannot be null"); + this.upperBound = null; + } + + /** + * Creates an instance composed of two distinct bounds. + * + * @throws NullPointerException if {@code lowerBound} or {@code upperBound} is null. + * @throws IllegalArgumentException if both {@code lowerBound} and {@code upperBound} are not + * unbounded and {@code lowerBound} is greater than {@code upperBound}. + */ + public DateRange(@NonNull DateRangeBound lowerBound, @NonNull DateRangeBound upperBound) { + Preconditions.checkNotNull(lowerBound, "lowerBound cannot be null"); + Preconditions.checkNotNull(upperBound, "upperBound cannot be null"); + if (!lowerBound.isUnbounded() + && !upperBound.isUnbounded() + && lowerBound.getTimestamp().compareTo(upperBound.getTimestamp()) >= 0) { + throw new IllegalArgumentException( + String.format( + "Lower bound of a date range should be before upper bound, got: [%s TO %s]", + lowerBound, upperBound)); + } + this.lowerBound = lowerBound; + this.upperBound = upperBound; + } + + /** Returns the lower bound of this range (inclusive). */ + @NonNull + public DateRangeBound getLowerBound() { + return lowerBound; + } + + /** + * Returns the upper bound of this range (inclusive), or empty if the range is {@linkplain + * #isSingleBounded() single-bounded}. + */ + @NonNull + public Optional getUpperBound() { + return Optional.ofNullable(upperBound); + } + + /** + * Returns whether this range is single-bounded, i.e. if the upper and lower bounds are identical. + */ + public boolean isSingleBounded() { + return upperBound == null; + } + + /** + * Returns the string representation of this range, in a format compatible with Apache Solr + * DateRageField syntax + * + * @see DateRangeBound#toString() + */ + @NonNull + @Override + public String toString() { + if (isSingleBounded()) { + return lowerBound.toString(); + } else { + return String.format("[%s TO %s]", lowerBound, upperBound); + } + } + + @Override + public boolean equals(@Nullable Object other) { + if (other == this) { + return true; + } else if (other instanceof DateRange) { + DateRange that = (DateRange) other; + return Objects.equals(this.lowerBound, that.lowerBound) + && Objects.equals(this.upperBound, that.upperBound); + + } else { + return false; + } + } + + @Override + public int hashCode() { + return Objects.hash(lowerBound, upperBound); + } + + private static ParseException newParseException(String message, int offset, Exception cause) { + ParseException parseException = new ParseException(message, offset); + parseException.initCause(cause); + return parseException; + } + + /** + * This object gets replaced by an internal proxy for serialization. + * + * @serialData the lower bound timestamp and precision, followed by the upper bound timestamp and + * precision, or two {@code null}s if the range is single-bounded. + */ + private Object writeReplace() { + return new SerializationProxy(this); + } + + private static class SerializationProxy implements Serializable { + + private static final long serialVersionUID = 1L; + + private final ZonedDateTime lowerBoundTimestamp; + private final DateRangePrecision lowerBoundPrecision; + private final ZonedDateTime upperBoundTimestamp; + private final DateRangePrecision upperBoundPrecision; + + SerializationProxy(DateRange input) { + this.lowerBoundTimestamp = input.lowerBound.getTimestamp(); + this.lowerBoundPrecision = input.lowerBound.getPrecision(); + if (input.upperBound != null) { + this.upperBoundTimestamp = input.upperBound.getTimestamp(); + this.upperBoundPrecision = input.upperBound.getPrecision(); + } else { + this.upperBoundTimestamp = null; + this.upperBoundPrecision = null; + } + } + + private Object readResolve() { + if (upperBoundTimestamp == null ^ upperBoundPrecision == null) { + // Should not happen, but protect against corrupted streams + throw new IllegalArgumentException( + "Invalid serialized form, upper bound timestamp and precision " + + "should be either both null or both non-null"); + } + + if (upperBoundTimestamp == null) { + return new DateRange(DateRangeBound.lowerBound(lowerBoundTimestamp, lowerBoundPrecision)); + } else { + return new DateRange( + DateRangeBound.lowerBound(lowerBoundTimestamp, lowerBoundPrecision), + DateRangeBound.upperBound(upperBoundTimestamp, upperBoundPrecision)); + } + } + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/data/time/DateRangeBound.java b/core/src/main/java/com/datastax/dse/driver/api/core/data/time/DateRangeBound.java new file mode 100644 index 00000000000..1621b8bf742 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/data/time/DateRangeBound.java @@ -0,0 +1,212 @@ +/* + * 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 com.datastax.dse.driver.api.core.data.time; + +import com.datastax.dse.driver.internal.core.search.DateRangeUtil; +import com.datastax.oss.driver.shaded.guava.common.base.Preconditions; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.text.ParseException; +import java.time.ZonedDateTime; +import java.util.Calendar; +import java.util.Objects; + +/** + * A date range bound. + * + *

It is composed of a {@link ZonedDateTime} field and a corresponding {@link + * DateRangePrecision}. + * + *

Date range bounds are inclusive. The special value {@link #UNBOUNDED} denotes an un unbounded + * (infinite) bound, represented by a {@code *} sign. + * + *

This class is immutable and thread-safe. + */ +public class DateRangeBound { + + /** + * The unbounded {@link DateRangeBound} instance. It is syntactically represented by a {@code *} + * (star) sign. + */ + public static final DateRangeBound UNBOUNDED = new DateRangeBound(); + + /** + * Parses the given input as a lower date range bound. + * + *

The input should be a Lucene-compliant + * string. + * + *

The returned bound will have its {@linkplain DateRangePrecision precision} inferred from the + * input, and its timestamp will be {@linkplain DateRangePrecision#roundDown(ZonedDateTime) + * rounded down} to that precision. + * + *

Note that, in order to align with the server's parsing behavior, dates will always be parsed + * in the UTC time zone. + * + * @throws NullPointerException if {@code lowerBound} is {@code null}. + * @throws ParseException if the given input cannot be parsed. + */ + @NonNull + public static DateRangeBound parseLowerBound(@NonNull String source) throws ParseException { + Preconditions.checkNotNull(source); + Calendar calendar = DateRangeUtil.parseCalendar(source); + DateRangePrecision precision = DateRangeUtil.getPrecision(calendar); + return (precision == null) + ? UNBOUNDED + : lowerBound(DateRangeUtil.toZonedDateTime(calendar), precision); + } + + /** + * Parses the given input as an upper date range bound. + * + *

The input should be a Lucene-compliant + * string. + * + *

The returned bound will have its {@linkplain DateRangePrecision precision} inferred from the + * input, and its timestamp will be {@linkplain DateRangePrecision#roundUp(ZonedDateTime)} rounded + * up} to that precision. + * + *

Note that, in order to align with the server's behavior (e.g. when using date range literals + * in CQL query strings), dates must always be in the UTC time zone: an optional trailing {@code + * Z}" is allowed, but no other time zone ID (not even {@code UTC}, {@code GMT} or {@code +00:00}) + * is permitted. + * + * @throws NullPointerException if {@code upperBound} is {@code null}. + * @throws ParseException if the given input cannot be parsed. + */ + public static DateRangeBound parseUpperBound(String source) throws ParseException { + Preconditions.checkNotNull(source); + Calendar calendar = DateRangeUtil.parseCalendar(source); + DateRangePrecision precision = DateRangeUtil.getPrecision(calendar); + return (precision == null) + ? UNBOUNDED + : upperBound(DateRangeUtil.toZonedDateTime(calendar), precision); + } + + /** + * Creates a date range lower bound from the given date and precision. Temporal fields smaller + * than the precision will be rounded down. + */ + public static DateRangeBound lowerBound(ZonedDateTime timestamp, DateRangePrecision precision) { + return new DateRangeBound(precision.roundDown(timestamp), precision); + } + + /** + * Creates a date range upper bound from the given date and precision. Temporal fields smaller + * than the precision will be rounded up. + */ + public static DateRangeBound upperBound(ZonedDateTime timestamp, DateRangePrecision precision) { + return new DateRangeBound(precision.roundUp(timestamp), precision); + } + + @Nullable private final ZonedDateTime timestamp; + @Nullable private final DateRangePrecision precision; + + private DateRangeBound(@NonNull ZonedDateTime timestamp, @NonNull DateRangePrecision precision) { + Preconditions.checkNotNull(timestamp); + Preconditions.checkNotNull(precision); + this.timestamp = timestamp; + this.precision = precision; + } + + // constructor used for the special UNBOUNDED value + private DateRangeBound() { + this.timestamp = null; + this.precision = null; + } + + /** Whether this bound is unbounded (i.e. denotes the special {@code *} value). */ + public boolean isUnbounded() { + return this.timestamp == null && this.precision == null; + } + + /** + * Returns the timestamp of this bound. + * + * @throws IllegalStateException if this bound is {@linkplain #isUnbounded() unbounded}. + */ + @NonNull + public ZonedDateTime getTimestamp() { + if (isUnbounded()) { + throw new IllegalStateException( + "Can't call this method on UNBOUNDED, use isUnbounded() to check first"); + } + assert timestamp != null; + return timestamp; + } + + /** + * Returns the precision of this bound. + * + * @throws IllegalStateException if this bound is {@linkplain #isUnbounded() unbounded}. + */ + @NonNull + public DateRangePrecision getPrecision() { + if (isUnbounded()) { + throw new IllegalStateException( + "Can't call this method on UNBOUNDED, use isUnbounded() to check first"); + } + assert precision != null; + return precision; + } + + /** + * Returns this bound as a Lucene-compliant string. + * + *

Unbounded bounds always return "{@code *}"; all other bounds are formatted in one of the + * common ISO-8601 datetime formats, depending on their precision. + * + *

Note that Lucene expects timestamps in UTC only. Timezone presence is always optional, and + * if present, it must be expressed with the symbol "Z" exclusively. Therefore this method does + * not include any timezone information in the returned string, except for bounds with {@linkplain + * DateRangePrecision#MILLISECOND millisecond} precision, where the symbol "Z" is always appended + * to the resulting string. + */ + @NonNull + @Override + public String toString() { + if (isUnbounded()) { + return "*"; + } else { + assert timestamp != null && precision != null; + return precision.format(timestamp); + } + } + + @Override + public boolean equals(@Nullable Object other) { + if (other == this) { + return true; + } else if (other instanceof DateRangeBound) { + DateRangeBound that = (DateRangeBound) other; + return Objects.equals(this.timestamp, that.timestamp) + && Objects.equals(this.precision, that.precision); + } else { + return false; + } + } + + @Override + public int hashCode() { + return Objects.hash(timestamp, precision); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/data/time/DateRangePrecision.java b/core/src/main/java/com/datastax/dse/driver/api/core/data/time/DateRangePrecision.java new file mode 100644 index 00000000000..ce811466c38 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/data/time/DateRangePrecision.java @@ -0,0 +1,197 @@ +/* + * 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 com.datastax.dse.driver.api.core.data.time; + +import com.datastax.dse.driver.internal.core.search.DateRangeUtil; +import com.datastax.oss.driver.shaded.guava.common.base.Preconditions; +import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableMap; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.temporal.ChronoField; +import java.time.temporal.ChronoUnit; +import java.util.Locale; +import java.util.Map; + +/** The precision of a {@link DateRangeBound}. */ +public enum DateRangePrecision { + MILLISECOND( + 0x06, + ChronoUnit.MILLIS, + new DateTimeFormatterBuilder() + .parseCaseSensitive() + .parseStrict() + .appendPattern("uuuu-MM-dd'T'HH:mm:ss.SSS") + .optionalStart() + .appendZoneId() + .optionalEnd() + .toFormatter() + .withZone(ZoneOffset.UTC) + .withLocale(Locale.ROOT)), + SECOND( + 0x05, + ChronoUnit.SECONDS, + new DateTimeFormatterBuilder() + .parseCaseSensitive() + .parseStrict() + .appendPattern("uuuu-MM-dd'T'HH:mm:ss") + .parseDefaulting(ChronoField.MILLI_OF_SECOND, 0) + .toFormatter() + .withZone(ZoneOffset.UTC) + .withLocale(Locale.ROOT)), + MINUTE( + 0x04, + ChronoUnit.MINUTES, + new DateTimeFormatterBuilder() + .parseCaseSensitive() + .parseStrict() + .appendPattern("uuuu-MM-dd'T'HH:mm") + .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0) + .parseDefaulting(ChronoField.MILLI_OF_SECOND, 0) + .toFormatter() + .withZone(ZoneOffset.UTC) + .withLocale(Locale.ROOT)), + HOUR( + 0x03, + ChronoUnit.HOURS, + new DateTimeFormatterBuilder() + .parseCaseSensitive() + .parseStrict() + .appendPattern("uuuu-MM-dd'T'HH") + .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0) + .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0) + .parseDefaulting(ChronoField.MILLI_OF_SECOND, 0) + .toFormatter() + .withZone(ZoneOffset.UTC) + .withLocale(Locale.ROOT)), + DAY( + 0x02, + ChronoUnit.DAYS, + new DateTimeFormatterBuilder() + .parseCaseSensitive() + .parseStrict() + .appendPattern("uuuu-MM-dd") + .parseDefaulting(ChronoField.HOUR_OF_DAY, 0) + .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0) + .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0) + .parseDefaulting(ChronoField.MILLI_OF_SECOND, 0) + .toFormatter() + .withZone(ZoneOffset.UTC) + .withLocale(Locale.ROOT)), + MONTH( + 0x01, + ChronoUnit.MONTHS, + new DateTimeFormatterBuilder() + .parseCaseSensitive() + .parseStrict() + .appendPattern("uuuu-MM") + .parseDefaulting(ChronoField.DAY_OF_MONTH, 1) + .parseDefaulting(ChronoField.HOUR_OF_DAY, 0) + .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0) + .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0) + .parseDefaulting(ChronoField.MILLI_OF_SECOND, 0) + .toFormatter() + .withZone(ZoneOffset.UTC) + .withLocale(Locale.ROOT)), + YEAR( + 0x00, + ChronoUnit.YEARS, + new DateTimeFormatterBuilder() + .parseCaseSensitive() + .parseStrict() + .appendPattern("uuuu") + .parseDefaulting(ChronoField.MONTH_OF_YEAR, 1) + .parseDefaulting(ChronoField.DAY_OF_MONTH, 1) + .parseDefaulting(ChronoField.HOUR_OF_DAY, 0) + .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0) + .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0) + .parseDefaulting(ChronoField.MILLI_OF_SECOND, 0) + .toFormatter() + .withZone(ZoneOffset.UTC) + .withLocale(Locale.ROOT)); + + private final byte encoding; + private final ChronoUnit roundingUnit; + // The formatter is only used for formatting (parsing is done with DateRangeUtil.parseCalendar to + // be exactly the same as DSE's). + // If that ever were to change, note that DateTimeFormatters with a time zone have a parsing bug + // in Java 8: the formatter's zone will always be used, even if the input string specifies one + // explicitly. + // See https://stackoverflow.com/questions/41999421 + private final DateTimeFormatter formatter; + + DateRangePrecision(int encoding, ChronoUnit roundingUnit, DateTimeFormatter formatter) { + this.encoding = (byte) encoding; + this.roundingUnit = roundingUnit; + this.formatter = formatter; + } + + private static final Map ENCODINGS; + + static { + ImmutableMap.Builder builder = ImmutableMap.builder(); + for (DateRangePrecision precision : values()) { + builder.put(precision.encoding, precision); + } + ENCODINGS = builder.build(); + } + + public static DateRangePrecision fromEncoding(byte encoding) { + DateRangePrecision precision = ENCODINGS.get(encoding); + if (precision == null) { + throw new IllegalArgumentException("Invalid precision encoding: " + encoding); + } + return precision; + } + + /** The code used to represent the precision when a date range is encoded to binary. */ + public byte getEncoding() { + return encoding; + } + + /** + * Rounds up the given timestamp to this precision. + * + *

Temporal fields smaller than this precision will be rounded up; other fields will be left + * untouched. + */ + @NonNull + public ZonedDateTime roundUp(@NonNull ZonedDateTime timestamp) { + Preconditions.checkNotNull(timestamp); + return DateRangeUtil.roundUp(timestamp, roundingUnit); + } + + /** + * Rounds down the given timestamp to this precision. + * + *

Temporal fields smaller than this precision will be rounded down; other fields will be left + * untouched. + */ + @NonNull + public ZonedDateTime roundDown(@NonNull ZonedDateTime timestamp) { + Preconditions.checkNotNull(timestamp); + return DateRangeUtil.roundDown(timestamp, roundingUnit); + } + + /** Formats the given timestamp according to this precision. */ + public String format(ZonedDateTime timestamp) { + return formatter.format(timestamp); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/graph/AsyncGraphResultSet.java b/core/src/main/java/com/datastax/dse/driver/api/core/graph/AsyncGraphResultSet.java new file mode 100644 index 00000000000..995de53959b --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/graph/AsyncGraphResultSet.java @@ -0,0 +1,101 @@ +/* + * 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 com.datastax.dse.driver.api.core.graph; + +import com.datastax.dse.driver.internal.core.graph.GraphExecutionInfoConverter; +import com.datastax.oss.driver.api.core.cql.ExecutionInfo; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.util.Iterator; +import java.util.concurrent.CompletionStage; + +/** + * The result of an asynchronous graph query. + * + *

The default implementation returned by the driver is not thread-safe: the iterable + * returned by {@link #currentPage()} should only be iterated by a single thread. However, if + * subsequent pages are requested via {@link #fetchNextPage()}, it's safe to process those new + * instances in other threads (as long as each individual page of results is not accessed + * concurrently). + * + * @see GraphResultSet + */ +public interface AsyncGraphResultSet { + + /** The execution information for this page of results. */ + @NonNull + default ExecutionInfo getRequestExecutionInfo() { + return GraphExecutionInfoConverter.convert(getExecutionInfo()); + } + + /** + * The execution information for this page of results. + * + * @deprecated Use {@link #getRequestExecutionInfo()} instead. + */ + @Deprecated + @NonNull + com.datastax.dse.driver.api.core.graph.GraphExecutionInfo getExecutionInfo(); + + /** How many rows are left before the current page is exhausted. */ + int remaining(); + + /** + * The nodes in the current page. To keep iterating beyond that, use {@link #hasMorePages()} and + * {@link #fetchNextPage()}. + * + *

Note that this method always returns the same object, and that that object can only be + * iterated once: nodes are "consumed" as they are read. + */ + @NonNull + Iterable currentPage(); + + /** + * Returns the next node, or {@code null} if the result set is exhausted. + * + *

This is convenient for queries that are known to return exactly one node. + */ + @Nullable + default GraphNode one() { + Iterator iterator = currentPage().iterator(); + return iterator.hasNext() ? iterator.next() : null; + } + + /** + * Whether there are more pages of results. If so, call {@link #fetchNextPage()} to fetch the next + * one asynchronously. + */ + boolean hasMorePages(); + + /** + * Fetch the next page of results asynchronously. + * + * @throws IllegalStateException if there are no more pages. Use {@link #hasMorePages()} to check + * if you can call this method. + */ + @NonNull + CompletionStage fetchNextPage() throws IllegalStateException; + + /** + * Cancels the query and asks the server to stop sending results. + * + *

At this time, graph queries are not paginated and the server sends all the results at once; + * therefore this method has no effect. + */ + void cancel(); +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/graph/BatchGraphStatement.java b/core/src/main/java/com/datastax/dse/driver/api/core/graph/BatchGraphStatement.java new file mode 100644 index 00000000000..2169dc5f053 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/graph/BatchGraphStatement.java @@ -0,0 +1,150 @@ +/* + * 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 com.datastax.dse.driver.api.core.graph; + +import com.datastax.dse.driver.internal.core.graph.DefaultBatchGraphStatement; +import com.datastax.oss.driver.api.core.cql.Statement; +import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableList; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.Collections; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; + +/** + * A graph statement that groups multiple mutating traversals together, to be executed in the + * same transaction. + * + *

It is reserved for graph mutations, and does not return any result. + * + *

All the mutations grouped in the batch will either all succeed, or they will all be discarded + * and return an error. + * + *

The default implementation returned by the driver is immutable and thread-safe. Each mutation + * operation returns a copy. If you chain many of those operations, it is recommended to use {@link + * #builder()} instead for better memory usage. + * + *

Typically used like so: + * + *

{@code
+ * import static com.datastax.dse.driver.api.core.graph.DseGraph.g;
+ *
+ * BatchGraphStatement statement =
+ *     BatchGraphStatement.builder()
+ *         .addTraversal(
+ *                 g.addV("person").property("name", "batch1").property("age", 1))
+ *         .addTraversal(
+ *                 g.addV("person").property("name", "batch2").property("age", 2))
+ *         .build();
+ *
+ * GraphResultSet graphResultSet = dseSession.execute(statement);
+ * }
+ * + * @see DseGraph#g + */ +public interface BatchGraphStatement + extends GraphStatement, Iterable { + + /** + * Create a new, empty instance. + * + *

Traversals can be added with {@link #addTraversal(GraphTraversal)}. + */ + @NonNull + static BatchGraphStatement newInstance() { + return new DefaultBatchGraphStatement( + ImmutableList.of(), + null, + null, + null, + Statement.NO_DEFAULT_TIMESTAMP, + null, + null, + Collections.emptyMap(), + null, + null, + null, + null, + null, + null); + } + + /** Create a new instance from the given list of traversals. */ + @NonNull + static BatchGraphStatement newInstance(@NonNull Iterable traversals) { + return new DefaultBatchGraphStatement( + traversals, + null, + null, + null, + Statement.NO_DEFAULT_TIMESTAMP, + null, + null, + Collections.emptyMap(), + null, + null, + null, + null, + null, + null); + } + + /** Create a new instance from the given list of traversals. */ + @NonNull + static BatchGraphStatement newInstance(@NonNull GraphTraversal... traversals) { + return newInstance(ImmutableList.copyOf(traversals)); + } + + /** + * Create a builder helper object to start creating a new instance. + * + *

Note that this builder is mutable and not thread-safe. + */ + @NonNull + static BatchGraphStatementBuilder builder() { + return new BatchGraphStatementBuilder(); + } + + /** + * Create a builder helper object to start creating a new instance with an existing statement as a + * template. The traversals and options set on the template will be copied for the new statement + * at the moment this method is called. + * + *

Note that this builder is mutable and not thread-safe. + */ + @NonNull + static BatchGraphStatementBuilder builder(@NonNull BatchGraphStatement template) { + return new BatchGraphStatementBuilder(template); + } + + /** + * Add a traversal to this statement. If many traversals need to be added, use a {@link + * #builder()}, or the {@link #addTraversals(Iterable)} method instead to avoid intermediary + * copies. + */ + @NonNull + BatchGraphStatement addTraversal(@NonNull GraphTraversal traversal); + + /** + * Adds several traversals to this statement. If this method is to be called many times, consider + * using a {@link #builder()} instead to avoid intermediary copies. + */ + @NonNull + BatchGraphStatement addTraversals(@NonNull Iterable traversals); + + /** Get the number of traversals already added to this statement. */ + int size(); +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/graph/BatchGraphStatementBuilder.java b/core/src/main/java/com/datastax/dse/driver/api/core/graph/BatchGraphStatementBuilder.java new file mode 100644 index 00000000000..ac1b85bdc71 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/graph/BatchGraphStatementBuilder.java @@ -0,0 +1,112 @@ +/* + * 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 com.datastax.dse.driver.api.core.graph; + +import com.datastax.dse.driver.internal.core.graph.DefaultBatchGraphStatement; +import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableList; +import edu.umd.cs.findbugs.annotations.NonNull; +import net.jcip.annotations.NotThreadSafe; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; + +/** + * A builder to create a batch graph statement. + * + *

This class is mutable and not thread-safe. + */ +@NotThreadSafe +public class BatchGraphStatementBuilder + extends GraphStatementBuilderBase { + + private ImmutableList.Builder traversalsBuilder = ImmutableList.builder(); + private int traversalsCount; + + public BatchGraphStatementBuilder() { + // nothing to do + } + + public BatchGraphStatementBuilder(BatchGraphStatement template) { + super(template); + traversalsBuilder.addAll(template); + traversalsCount = template.size(); + } + + /** Add a traversal to this builder to include in the generated {@link BatchGraphStatement}. */ + @NonNull + public BatchGraphStatementBuilder addTraversal(@NonNull GraphTraversal traversal) { + traversalsBuilder.add(traversal); + traversalsCount += 1; + return this; + } + + /** + * Add several traversals to this builder to include in the generated {@link BatchGraphStatement}. + */ + @NonNull + public BatchGraphStatementBuilder addTraversals(@NonNull Iterable traversals) { + for (GraphTraversal traversal : traversals) { + traversalsBuilder.add(traversal); + traversalsCount += 1; + } + return this; + } + + /** + * Add several traversals to this builder to include in the generated {@link BatchGraphStatement}. + */ + @NonNull + public BatchGraphStatementBuilder addTraversals(@NonNull GraphTraversal... traversals) { + for (GraphTraversal traversal : traversals) { + traversalsBuilder.add(traversal); + traversalsCount += 1; + } + return this; + } + + /** Clears all the traversals previously added to this builder. */ + @NonNull + public BatchGraphStatementBuilder clearTraversals() { + traversalsBuilder = ImmutableList.builder(); + traversalsCount = 0; + return this; + } + + /** Returns the number of traversals added to this statement so far. */ + public int getTraversalsCount() { + return traversalsCount; + } + + @NonNull + @Override + public BatchGraphStatement build() { + return new DefaultBatchGraphStatement( + traversalsBuilder.build(), + isIdempotent, + timeout, + node, + timestamp, + executionProfile, + executionProfileName, + buildCustomPayload(), + graphName, + traversalSource, + subProtocol, + consistencyLevel, + readConsistencyLevel, + writeConsistencyLevel); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/graph/DseGraph.java b/core/src/main/java/com/datastax/dse/driver/api/core/graph/DseGraph.java new file mode 100644 index 00000000000..dd1dbe95bc8 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/graph/DseGraph.java @@ -0,0 +1,79 @@ +/* + * 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 com.datastax.dse.driver.api.core.graph; + +import com.datastax.dse.driver.internal.core.graph.DefaultDseRemoteConnectionBuilder; +import com.datastax.oss.driver.api.core.CqlSession; +import org.apache.tinkerpop.gremlin.process.traversal.AnonymousTraversalSource; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; +import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph; + +/** + * General purpose utility class for interaction with DSE Graph via the DataStax Enterprise Java + * driver. + */ +public class DseGraph { + + /** + * IMPORTANT: As of Tinkerpop 3.3.5, you should no longer use this shortcut if you intend + * to connect the traversal to DSE Graph using a {@linkplain + * org.apache.tinkerpop.gremlin.process.remote.RemoteConnection remote connection}, for example + * via the {@link #remoteConnectionBuilder} method declared below. Instead of: + * + *

{@code
+   * DseSession session = ...;
+   * RemoteConnection remoteConnection = DseGraph.remoteConnectionBuilder(session).build();
+   * GraphTraversalSource g = DseGraph.g.withRemote(remoteConnection);
+   * }
+ * + * You should now use {@link AnonymousTraversalSource#traversal()}, and adopt the following idiom: + * + *
{@code
+   * DseSession session = ...;
+   * RemoteConnection remoteConnection = DseGraph.remoteConnectionBuilder(session).build();
+   * GraphTraversalSource g = AnonymousTraversalSource.traversal().withRemote(remoteConnection);
+   * }
+ * + * A general-purpose shortcut for a non-connected TinkerPop {@link GraphTraversalSource} + * based on an immutable empty graph. This is really just a shortcut to {@code + * EmptyGraph.instance().traversal();}. + * + *

It can be used to create {@link FluentGraphStatement} instances (recommended); for ease of + * use you may statically import this variable. + * + *

Calling {@code g.getGraph()} will return a local immutable empty graph which is in no way + * connected to the DSE Graph server, it will not allow to modify a DSE Graph directly. To act on + * data stored in DSE Graph you must use {@linkplain + * org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal traversal}s such as + * {@code DseGraph.g.V()}, {@code DseGraph.g.addV/addE()}. + */ + public static final GraphTraversalSource g = EmptyGraph.instance().traversal(); + + /** + * Returns a builder helper class to help create {@link + * org.apache.tinkerpop.gremlin.process.remote.RemoteConnection} implementations that seamlessly + * connect to DSE Graph using the {@link CqlSession} in parameter. + */ + public static DseGraphRemoteConnectionBuilder remoteConnectionBuilder(CqlSession dseSession) { + return new DefaultDseRemoteConnectionBuilder(dseSession); + } + + private DseGraph() { + // nothing to do + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/graph/DseGraphRemoteConnectionBuilder.java b/core/src/main/java/com/datastax/dse/driver/api/core/graph/DseGraphRemoteConnectionBuilder.java new file mode 100644 index 00000000000..c4210a5b3dd --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/graph/DseGraphRemoteConnectionBuilder.java @@ -0,0 +1,61 @@ +/* + * 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 com.datastax.dse.driver.api.core.graph; + +import com.datastax.oss.driver.api.core.CqlSession; +import com.datastax.oss.driver.api.core.config.DriverExecutionProfile; +import org.apache.tinkerpop.gremlin.process.remote.RemoteConnection; + +/** + * A builder helper to create a {@link RemoteConnection} that will be used to build + * implicitly-executing fluent traversals. + * + *

To create an instance of this, use the {@link DseGraph#remoteConnectionBuilder(CqlSession)} + * method: + * + *

{@code
+ * DseSession dseSession = DseSession.builder().build();
+ * GraphTraversalSource g = AnonymousTraversalSource.traversal().withRemote(DseGraph.remoteConnectionBuilder(dseSession).build());
+ * List vertices = g.V().hasLabel("person").toList();
+ * }
+ * + * @see CqlSession + */ +public interface DseGraphRemoteConnectionBuilder { + + /** Build the remote connection that was configured with this builder. */ + RemoteConnection build(); + + /** + * Set a configuration profile that will be used for every traversal built using the remote + * connection. + * + *

For the list of options available for Graph requests, see the {@code reference.conf} + * configuration file. + */ + DseGraphRemoteConnectionBuilder withExecutionProfile(DriverExecutionProfile executionProfile); + + /** + * Set the name of an execution profile that will be used for every traversal using from the + * remote connection. Named profiles are pre-defined in the driver configuration. + * + *

For the list of options available for Graph requests, see the {@code reference.conf} + * configuration file. + */ + DseGraphRemoteConnectionBuilder withExecutionProfileName(String executionProfileName); +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/graph/FluentGraphStatement.java b/core/src/main/java/com/datastax/dse/driver/api/core/graph/FluentGraphStatement.java new file mode 100644 index 00000000000..051c6501c65 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/graph/FluentGraphStatement.java @@ -0,0 +1,93 @@ +/* + * 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 com.datastax.dse.driver.api.core.graph; + +import com.datastax.dse.driver.internal.core.graph.DefaultFluentGraphStatement; +import com.datastax.oss.driver.api.core.cql.Statement; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.Collections; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; + +/** + * A graph statement that uses a TinkerPop {@link GraphTraversal} as the query. + * + *

Typically used like so: + * + *

{@code
+ * import static com.datastax.dse.driver.api.core.graph.DseGraph.g;
+ *
+ * FluentGraphStatement statement = FluentGraphStatement.newInstance(g.V().has("name", "marko"));
+ *
+ * GraphResultSet graphResultSet = dseSession.execute(statement);
+ * }
+ * + * @see DseGraph#g + */ +public interface FluentGraphStatement extends GraphStatement { + + /** + * Create a new instance from the given traversal. + * + *

Use {@link #builder(GraphTraversal)} if you want to set more options before building the + * final statement instance. + */ + @NonNull + static FluentGraphStatement newInstance(@NonNull GraphTraversal traversal) { + return new DefaultFluentGraphStatement( + traversal, + null, + null, + null, + Statement.NO_DEFAULT_TIMESTAMP, + null, + null, + Collections.emptyMap(), + null, + null, + null, + null, + null, + null); + } + + /** + * Create a builder object to start creating a new instance from the given traversal. + * + *

Note that this builder is mutable and not thread-safe. + */ + @NonNull + static FluentGraphStatementBuilder builder(@NonNull GraphTraversal traversal) { + return new FluentGraphStatementBuilder(traversal); + } + + /** + * Create a builder helper object to start creating a new instance with an existing statement as a + * template. The traversal and options set on the template will be copied for the new statement at + * the moment this method is called. + * + *

Note that this builder is mutable and not thread-safe. + */ + @NonNull + static FluentGraphStatementBuilder builder(@NonNull FluentGraphStatement template) { + return new FluentGraphStatementBuilder(template); + } + + /** The underlying TinkerPop object representing the traversal executed by this statement. */ + @NonNull + GraphTraversal getTraversal(); +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/graph/FluentGraphStatementBuilder.java b/core/src/main/java/com/datastax/dse/driver/api/core/graph/FluentGraphStatementBuilder.java new file mode 100644 index 00000000000..59e588c564a --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/graph/FluentGraphStatementBuilder.java @@ -0,0 +1,64 @@ +/* + * 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 com.datastax.dse.driver.api.core.graph; + +import com.datastax.dse.driver.internal.core.graph.DefaultFluentGraphStatement; +import edu.umd.cs.findbugs.annotations.NonNull; +import net.jcip.annotations.NotThreadSafe; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; + +/** + * A builder to create a fluent graph statement. + * + *

This class is mutable and not thread-safe. + */ +@NotThreadSafe +public class FluentGraphStatementBuilder + extends GraphStatementBuilderBase { + + private GraphTraversal traversal; + + public FluentGraphStatementBuilder(@NonNull GraphTraversal traversal) { + this.traversal = traversal; + } + + public FluentGraphStatementBuilder(@NonNull FluentGraphStatement template) { + super(template); + this.traversal = template.getTraversal(); + } + + @NonNull + @Override + public FluentGraphStatement build() { + return new DefaultFluentGraphStatement( + this.traversal, + isIdempotent, + timeout, + node, + timestamp, + executionProfile, + executionProfileName, + buildCustomPayload(), + graphName, + traversalSource, + subProtocol, + consistencyLevel, + readConsistencyLevel, + writeConsistencyLevel); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/graph/GraphExecutionInfo.java b/core/src/main/java/com/datastax/dse/driver/api/core/graph/GraphExecutionInfo.java new file mode 100644 index 00000000000..758f6b358ed --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/graph/GraphExecutionInfo.java @@ -0,0 +1,94 @@ +/* + * 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 com.datastax.dse.driver.api.core.graph; + +import com.datastax.oss.driver.api.core.DefaultProtocolVersion; +import com.datastax.oss.driver.api.core.metadata.Node; +import com.datastax.oss.driver.api.core.specex.SpeculativeExecutionPolicy; +import java.nio.ByteBuffer; +import java.util.List; +import java.util.Map; + +/** + * Information about the execution of a graph statement. + * + * @deprecated This interface is not used by any driver component anymore; the driver now exposes + * instances of {@link com.datastax.oss.driver.api.core.cql.ExecutionInfo} for all Graph + * queries. + */ +@Deprecated +public interface GraphExecutionInfo { + + /** The statement that was executed. */ + GraphStatement getStatement(); + + /** The node that was used as a coordinator to successfully complete the query. */ + Node getCoordinator(); + + /** + * The number of speculative executions that were started for this query. + * + *

This does not include the initial, normal execution of the query. Therefore, if speculative + * executions are disabled, this will always be 0. If they are enabled and one speculative + * execution was triggered in addition to the initial execution, this will be 1, etc. + * + * @see SpeculativeExecutionPolicy + */ + int getSpeculativeExecutionCount(); + + /** + * The index of the execution that completed this query. + * + *

0 represents the initial, normal execution of the query, 1 the first speculative execution, + * etc. + * + * @see SpeculativeExecutionPolicy + */ + int getSuccessfulExecutionIndex(); + + /** + * The errors encountered on previous coordinators, if any. + * + *

The list is in chronological order, based on the time that the driver processed the error + * responses. If speculative executions are enabled, they run concurrently so their errors will be + * interleaved. A node can appear multiple times (if the retry policy decided to retry on the same + * node). + */ + List> getErrors(); + + /** + * The server-side warnings for this query, if any (otherwise the list will be empty). + * + *

This feature is only available with {@link DefaultProtocolVersion#V4} or above; with lower + * versions, this list will always be empty. + */ + List getWarnings(); + + /** + * The custom payload sent back by the server with the response, if any (otherwise the map will be + * empty). + * + *

This method returns a read-only view of the original map, but its values remain inherently + * mutable. If multiple clients will read these values, care should be taken not to corrupt the + * data (in particular, preserve the indices by calling {@link ByteBuffer#duplicate()}). + * + *

This feature is only available with {@link DefaultProtocolVersion#V4} or above; with lower + * versions, this map will always be empty. + */ + Map getIncomingPayload(); +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/graph/GraphNode.java b/core/src/main/java/com/datastax/dse/driver/api/core/graph/GraphNode.java new file mode 100644 index 00000000000..97d48a6b04d --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/graph/GraphNode.java @@ -0,0 +1,234 @@ +/* + * 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 com.datastax.dse.driver.api.core.graph; + +import com.datastax.oss.driver.api.core.type.reflect.GenericType; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.apache.tinkerpop.gremlin.process.traversal.Path; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Property; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.structure.VertexProperty; + +/** + * A node in a tree-like structure representing a Graph or a Graph component. + * + *

It can be: + * + *

    + *
  • a scalar value of a primitive type (boolean, string, int, long, double); + *
  • a graph element (vertex, edge, path or property); + *
  • a list of nodes; + *
  • a set of nodes; + *
  • a map of nodes. + *
+ * + * This interface provides test methods to find out what a node represents, and conversion methods + * to cast it to a particular Java type. Two generic methods {@link #as(Class)} and {@link + * #as(GenericType)} can produce any arbitrary Java type, provided that the underlying serialization + * runtime has been correctly configured to support the requested conversion. + */ +public interface GraphNode { + + /** Whether this node represents a {@code null} value. */ + boolean isNull(); + + /** + * Returns {@code true} if this node is a {@link Map}, and {@code false} otherwise. + * + *

If this method returns {@code true}, you can convert this node with {@link #asMap()}, or use + * {@link #keys()} and {@link #getByKey(Object)} to access the individual fields (note that + * entries are not ordered, so {@link #getByIndex(int)} does not work). + */ + boolean isMap(); + + /** The keys of this map node, or an empty iterator if it is not a map. */ + Iterable keys(); + + /** + * Returns the value for the given key as a node. + * + *

If this node is not a map, or does not contain the specified key, {@code null} is returned. + * + *

If the property value has been explicitly set to {@code null}, implementors may return a + * special "null node" instead of {@code null}. + */ + GraphNode getByKey(Object key); + + /** Deserializes and returns this node as a {@link Map}. */ + Map asMap(); + + /** + * Returns {@code true} if this node is a {@link List}, and {@code false} otherwise. + * + *

If this method returns {@code true}, you can convert this node with {@link #asList()}, or + * use {@link #size()} and {@link #getByIndex(int)} to access the individual fields. + */ + boolean isList(); + + /** The size of the current node, if it is a list or map, or {@code 0} otherwise. */ + int size(); + + /** + * Returns the element at the given index as a node. + * + *

If this node is not a list, or {@code index} is out of bounds (i.e. less than zero or {@code + * >= size()}, {@code null} is returned; no exception will be thrown. + * + *

If the requested element has been explicitly set to {@code null}, implementors may return a + * special "null node" instead of {@code null}. + */ + GraphNode getByIndex(int index); + + /** Deserializes and returns this node as a {@link List}. */ + List asList(); + + /** + * Returns {@code true} if this node is a simple scalar value, (i.e., string, boolean or number), + * and {@code false} otherwise. + * + *

If this method returns {@code true}, you can convert this node with {@link #asString()}, + * {@link #asBoolean()}, {@link #asInt()}, {@link #asLong()} or {@link #asDouble()}. + */ + boolean isValue(); + + /** + * Returns this node as an integer. + * + *

If the underlying object is not convertible to integer, implementors may choose to either + * throw {@link ClassCastException} or return [null | empty | some default value], whichever is + * deemed more appropriate. + */ + int asInt(); + + /** + * Returns this node as a boolean. + * + *

If the underlying object is not convertible to boolean, implementors may choose to either + * throw {@link ClassCastException} or return [null | empty | some default value], whichever is + * deemed more appropriate. + */ + boolean asBoolean(); + + /** + * Returns this node as a long integer. + * + *

If the underlying object is not convertible to long, implementors may choose to either throw + * {@link ClassCastException} or return [null | empty | some default value], whichever is deemed + * more appropriate. + */ + long asLong(); + + /** + * Returns this node as a long integer. + * + *

If the underlying object is not convertible to double, implementors may choose to either + * throw {@link ClassCastException} or return [null | empty | some default value], whichever is + * deemed more appropriate. + */ + double asDouble(); + + /** + * A valid string representation of this node. + * + *

If the underlying object is not convertible to a string, implementors may choose to either + * throw {@link ClassCastException} or return an empty string, whichever is deemed more + * appropriate. + */ + String asString(); + + /** + * Deserializes and returns this node as an instance of {@code clazz}. + * + *

Before attempting such a conversion, there must be an appropriate converter configured on + * the underlying serialization runtime. + */ + ResultT as(Class clazz); + + /** + * Deserializes and returns this node as an instance of the given {@link GenericType type}. + * + *

Before attempting such a conversion, there must be an appropriate converter configured on + * the underlying serialization runtime. + */ + ResultT as(GenericType type); + + /** + * Returns {@code true} if this node is a {@link Vertex}, and {@code false} otherwise. + * + *

If this method returns {@code true}, then {@link #asVertex()} can be safely called. + */ + boolean isVertex(); + + /** Returns this node as a Tinkerpop {@link Vertex}. */ + Vertex asVertex(); + + /** + * Returns {@code true} if this node is a {@link Edge}, and {@code false} otherwise. + * + *

If this method returns {@code true}, then {@link #asEdge()} can be safely called. + */ + boolean isEdge(); + + /** Returns this node as a Tinkerpop {@link Edge}. */ + Edge asEdge(); + + /** + * Returns {@code true} if this node is a {@link Path}, and {@code false} otherwise. + * + *

If this method returns {@code true}, then {@link #asPath()} can be safely called. + */ + boolean isPath(); + + /** Returns this node as a Tinkerpop {@link Path}. */ + Path asPath(); + + /** + * Returns {@code true} if this node is a {@link Property}, and {@code false} otherwise. + * + *

If this method returns {@code true}, then {@link #asProperty()} can be safely called. + */ + boolean isProperty(); + + /** Returns this node as a Tinkerpop {@link Property}. */ + Property asProperty(); + + /** + * Returns {@code true} if this node is a {@link VertexProperty}, and {@code false} otherwise. + * + *

If this method returns {@code true}, then {@link #asVertexProperty()} ()} can be safely + * called. + */ + boolean isVertexProperty(); + + /** Returns this node as a Tinkerpop {@link VertexProperty}. */ + VertexProperty asVertexProperty(); + + /** + * Returns {@code true} if this node is a {@link Set}, and {@code false} otherwise. + * + *

If this method returns {@code true}, you can convert this node with {@link #asSet()}, or use + * {@link #size()}. + */ + boolean isSet(); + + /** Deserializes and returns this node as a {@link Set}. */ + Set asSet(); +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/graph/GraphResultSet.java b/core/src/main/java/com/datastax/dse/driver/api/core/graph/GraphResultSet.java new file mode 100644 index 00000000000..d9c8d8fa460 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/graph/GraphResultSet.java @@ -0,0 +1,93 @@ +/* + * 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 com.datastax.dse.driver.api.core.graph; + +import com.datastax.dse.driver.internal.core.graph.GraphExecutionInfoConverter; +import com.datastax.oss.driver.api.core.cql.ExecutionInfo; +import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableList; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +/** + * The result of a synchronous Graph query. + * + *

This object is a container for {@link GraphNode} objects that will contain the data returned + * by Graph queries. + * + *

Note that this object can only be iterated once: items are "consumed" as they are read, + * subsequent calls to {@code iterator()} will return the same iterator instance. + * + *

The default implementation returned by the driver is not thread-safe. It can only be + * iterated by the thread that invoked {@code dseSession.execute}. + * + * @see GraphNode + * @see GraphSession#execute(GraphStatement) + */ +public interface GraphResultSet extends Iterable { + + /** + * Returns the next node, or {@code null} if the result set is exhausted. + * + *

This is convenient for queries that are known to return exactly one row, for example count + * queries. + */ + @Nullable + default GraphNode one() { + Iterator graphNodeIterator = iterator(); + return graphNodeIterator.hasNext() ? graphNodeIterator.next() : null; + } + + /** + * Returns all the remaining nodes as a list; not recommended for paginated queries that return + * a large number of nodes. + * + *

At this time (DSE 6.0.0), graph queries are not paginated and the server sends all the + * results at once. + */ + @NonNull + default List all() { + if (!iterator().hasNext()) { + return Collections.emptyList(); + } + return ImmutableList.copyOf(this); + } + + /** + * Cancels the query and asks the server to stop sending results. + * + *

At this time (DSE 6.0.0), graph queries are not paginated and the server sends all the + * results at once; therefore this method has no effect. + */ + void cancel(); + + /** + * The execution information for the query that have been performed to assemble this result set. + */ + @NonNull + default ExecutionInfo getRequestExecutionInfo() { + return GraphExecutionInfoConverter.convert(getExecutionInfo()); + } + + /** @deprecated Use {@link #getRequestExecutionInfo()} instead. */ + @Deprecated + @NonNull + com.datastax.dse.driver.api.core.graph.GraphExecutionInfo getExecutionInfo(); +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/graph/GraphSession.java b/core/src/main/java/com/datastax/dse/driver/api/core/graph/GraphSession.java new file mode 100644 index 00000000000..b985bc56353 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/graph/GraphSession.java @@ -0,0 +1,87 @@ +/* + * 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 com.datastax.dse.driver.api.core.graph; + +import com.datastax.oss.driver.api.core.CqlSession; +import com.datastax.oss.driver.api.core.session.Session; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.Objects; +import java.util.concurrent.CompletionStage; + +/** + * A session that has the ability to execute DSE Graph requests. + * + *

Generally this interface won't be referenced directly in an application; instead, you should + * use {@link CqlSession}, which is a combination of this interface and many others for a more + * integrated usage of DataStax Enterprise's multi-model database via a single entry point. However, + * it is still possible to cast a {@code CqlSession} to a {@code GraphSession} to only expose the + * DSE Graph execution methods. + */ +public interface GraphSession extends Session { + + /** + * Executes a graph statement synchronously (the calling thread blocks until the result becomes + * available). + * + *

The driver provides different kinds of graph statements: + * + *

    + *
  • {@link FluentGraphStatement} (recommended): wraps a fluent TinkerPop {@linkplain + * org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal traversal}; + *
  • {@link BatchGraphStatement}: groups together multiple mutating traversals ({@code + * g.addV()/g.addE()}) inside a single transaction and avoids multiple client-server + * round-trips. Improves performance in data ingestion scenarios; + *
  • {@link ScriptGraphStatement}: wraps a Gremlin-groovy script provided as a plain Java + * string. Required for administrative queries such as creating/dropping a graph, + * configuration and schema. + *
+ * + *

This feature is only available with DataStax Enterprise. Executing graph queries against an + * Apache Cassandra® cluster will result in a runtime error. + * + * @see GraphResultSet + * @param graphStatement the graph query to execute (that can be any {@code GraphStatement}). + * @return the result of the graph query. That result will never be null but can be empty. + */ + @NonNull + default GraphResultSet execute(@NonNull GraphStatement graphStatement) { + return Objects.requireNonNull( + execute(graphStatement, GraphStatement.SYNC), + "The graph processor should never return a null result"); + } + + /** + * Executes a graph statement asynchronously (the call returns as soon as the statement was sent, + * generally before the result is available). + * + *

This feature is only available with DataStax Enterprise. Executing graph queries against an + * Apache Cassandra® cluster will result in a runtime error. + * + * @see #execute(GraphStatement) + * @see AsyncGraphResultSet + * @param graphStatement the graph query to execute (that can be any {@code GraphStatement}). + * @return the {@code CompletionStage} on the result of the graph query. + */ + @NonNull + default CompletionStage executeAsync( + @NonNull GraphStatement graphStatement) { + return Objects.requireNonNull( + execute(graphStatement, GraphStatement.ASYNC), + "The graph processor should never return a null result"); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/graph/GraphStatement.java b/core/src/main/java/com/datastax/dse/driver/api/core/graph/GraphStatement.java new file mode 100644 index 00000000000..f770469b824 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/graph/GraphStatement.java @@ -0,0 +1,378 @@ +/* + * 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 com.datastax.dse.driver.api.core.graph; + +import com.datastax.oss.driver.api.core.ConsistencyLevel; +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.api.core.CqlSession; +import com.datastax.oss.driver.api.core.NoNodeAvailableException; +import com.datastax.oss.driver.api.core.config.DriverExecutionProfile; +import com.datastax.oss.driver.api.core.loadbalancing.LoadBalancingPolicy; +import com.datastax.oss.driver.api.core.loadbalancing.NodeDistance; +import com.datastax.oss.driver.api.core.metadata.Node; +import com.datastax.oss.driver.api.core.metadata.token.Token; +import com.datastax.oss.driver.api.core.session.Request; +import com.datastax.oss.driver.api.core.session.Session; +import com.datastax.oss.driver.api.core.specex.SpeculativeExecutionPolicy; +import com.datastax.oss.driver.api.core.type.reflect.GenericType; +import edu.umd.cs.findbugs.annotations.CheckReturnValue; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.nio.ByteBuffer; +import java.time.Duration; +import java.util.Map; +import java.util.concurrent.CompletionStage; + +/** + * A request to execute a DSE Graph query. + * + * @param the "self type" used for covariant returns in subtypes. + */ +public interface GraphStatement> extends Request { + + /** + * The type returned when a graph statement is executed synchronously. + * + *

Most users won't use this explicitly. It is needed for the generic execute method ({@link + * Session#execute(Request, GenericType)}), but graph statements will generally be run with one of + * the DSE driver's built-in helper methods (such as {@link CqlSession#execute(GraphStatement)}). + */ + GenericType SYNC = GenericType.of(GraphResultSet.class); + + /** + * The type returned when a graph statement is executed asynchronously. + * + *

Most users won't use this explicitly. It is needed for the generic execute method ({@link + * Session#execute(Request, GenericType)}), but graph statements will generally be run with one of + * the DSE driver's built-in helper methods (such as {@link + * CqlSession#executeAsync(GraphStatement)}). + */ + GenericType> ASYNC = + new GenericType>() {}; + + /** + * Set the idempotence to use for execution. + * + *

Idempotence defines whether it will be possible to speculatively re-execute the statement, + * based on a {@link SpeculativeExecutionPolicy}. + * + *

All the driver's built-in implementations are immutable, and return a new instance from this + * method. However custom implementations may choose to be mutable and return the same instance. + * + * @param idempotent a boolean instance to set a statement-specific value, or {@code null} to use + * the default idempotence defined in the configuration. + */ + @NonNull + @CheckReturnValue + SelfT setIdempotent(@Nullable Boolean idempotent); + + /** + * {@inheritDoc} + * + *

Note that, if this method returns {@code null}, graph statements fall back to a dedicated + * configuration option: {@code basic.graph.timeout}. See {@code reference.conf} in the DSE driver + * distribution for more details. + */ + @Nullable + @Override + Duration getTimeout(); + + /** + * Sets how long to wait for this request to complete. This is a global limit on the duration of a + * session.execute() call, including any retries the driver might do. + * + *

All the driver's built-in implementations are immutable, and return a new instance from this + * method. However custom implementations may choose to be mutable and return the same instance. + * + * @param newTimeout the timeout to use, or {@code null} to use the default value defined in the + * configuration. + * @see #getTimeout() + */ + @NonNull + @CheckReturnValue + SelfT setTimeout(@Nullable Duration newTimeout); + + /** + * Sets the {@link Node} that should handle this query. + * + *

In the general case, use of this method is heavily discouraged and should only be + * used in specific cases, such as applying a series of schema changes, which may be advantageous + * to execute in sequence on the same node. + * + *

Configuring a specific node causes the configured {@link LoadBalancingPolicy} to be + * completely bypassed. However, if the load balancing policy dictates that the node is at + * distance {@link NodeDistance#IGNORED} or there is no active connectivity to the node, the + * request will fail with a {@link NoNodeAvailableException}. + * + *

All the driver's built-in implementations are immutable, and return a new instance from this + * method. However custom implementations may choose to be mutable and return the same instance. + * + * @param newNode The node that should be used to handle executions of this statement or null to + * delegate to the configured load balancing policy. + */ + @NonNull + @CheckReturnValue + SelfT setNode(@Nullable Node newNode); + + /** + * Get the timestamp set on the statement. + * + *

By default, if left unset, the value returned by this is {@code Long.MIN_VALUE}, which means + * that the timestamp will be set via the Timestamp Generator. + * + * @return the timestamp set on this statement. + */ + long getTimestamp(); + + /** + * Set the timestamp to use for execution. + * + *

By default the timestamp generator (see reference config file) will be used for timestamps, + * unless set explicitly via this method. + * + *

All the driver's built-in implementations are immutable, and return a new instance from this + * method. However custom implementations may choose to be mutable and return the same instance. + */ + @CheckReturnValue + SelfT setTimestamp(long timestamp); + + /** + * Sets the configuration profile to use for execution. + * + *

All the driver's built-in implementations are immutable, and return a new instance from this + * method. However custom implementations may choose to be mutable and return the same instance. + */ + @NonNull + @CheckReturnValue + SelfT setExecutionProfile(@Nullable DriverExecutionProfile executionProfile); + + /** + * Sets the name of the driver configuration profile that will be used for execution. + * + *

For all the driver's built-in implementations, this method has no effect if {@link + * #setExecutionProfile} has been called with a non-null argument. + * + *

All the driver's built-in implementations are immutable, and return a new instance from this + * method. However custom implementations may choose to be mutable and return the same instance. + */ + @NonNull + @CheckReturnValue + SelfT setExecutionProfileName(@Nullable String name); + + /** + * Sets the custom payload to use for execution. + * + *

This is intended for advanced use cases, such as tools with very advanced knowledge of DSE + * Graph, and reserved for internal settings like transaction settings. Note that the driver also + * adds graph-related options to the payload, in addition to the ones provided here; it won't + * override any option that is already present. + * + *

All the driver's built-in statement implementations are immutable, and return a new instance + * from this method. However custom implementations may choose to be mutable and return the same + * instance. + * + *

Note that it's your responsibility to provide a thread-safe map. This can be achieved with a + * concurrent or immutable implementation, or by making it effectively immutable (meaning that + * it's never modified after being set on the statement). + * + *

All the driver's built-in implementations are immutable, and return a new instance from this + * method. However custom implementations may choose to be mutable and return the same instance. + */ + @NonNull + @CheckReturnValue + SelfT setCustomPayload(@NonNull Map newCustomPayload); + + /** + * The name of the graph to use for this statement. + * + *

This is the programmatic equivalent of the configuration option {@code basic.graph.name}, + * and takes precedence over it. That is, if this property is non-null, then the configuration + * will be ignored. + */ + @Nullable + String getGraphName(); + + /** + * Sets the graph name. + * + *

All the driver's built-in implementations are immutable, and return a new instance from this + * method. However custom implementations may choose to be mutable and return the same instance. + * + * @see #getGraphName() + */ + @NonNull + @CheckReturnValue + SelfT setGraphName(@Nullable String newGraphName); + + /** + * The name of the traversal source to use for this statement. + * + *

This is the programmatic equivalent of the configuration option {@code + * basic.graph.traversal-source}, and takes precedence over it. That is, if this property is + * non-null, then the configuration will be ignored. + */ + @Nullable + String getTraversalSource(); + + /** + * Sets the traversal source. + * + *

All the driver's built-in implementations are immutable, and return a new instance from this + * method. However custom implementations may choose to be mutable and return the same instance. + * + * @see #getTraversalSource() + */ + @NonNull + @CheckReturnValue + SelfT setTraversalSource(@Nullable String newTraversalSource); + + /** + * The DSE graph sub-protocol to use for this statement. + * + *

This is the programmatic equivalent of the configuration option {@code + * advanced.graph.sub-protocol}, and takes precedence over it. That is, if this property is + * non-null, then the configuration will be ignored. + */ + @Nullable + String getSubProtocol(); + + /** + * Sets the sub-protocol. + * + *

All the driver's built-in implementations are immutable, and return a new instance from this + * method. However custom implementations may choose to be mutable and return the same instance. + * + * @see #getSubProtocol() + */ + @NonNull + @CheckReturnValue + SelfT setSubProtocol(@Nullable String newSubProtocol); + + /** + * Returns the consistency level to use for the statement. + * + *

This is the programmatic equivalent of the configuration option {@code + * basic.request.consistency}, and takes precedence over it. That is, if this property is + * non-null, then the configuration will be ignored. + */ + @Nullable + ConsistencyLevel getConsistencyLevel(); + + /** + * Sets the consistency level to use for this statement. + * + *

All the driver's built-in implementations are immutable, and return a new instance from this + * method. However custom implementations may choose to be mutable and return the same instance. + * + * @param newConsistencyLevel the consistency level to use, or null to use the default value + * defined in the configuration. + * @see #getConsistencyLevel() + */ + @CheckReturnValue + SelfT setConsistencyLevel(@Nullable ConsistencyLevel newConsistencyLevel); + + /** + * The consistency level to use for the internal read queries that will be produced by this + * statement. + * + *

This is the programmatic equivalent of the configuration option {@code + * basic.graph.read-consistency-level}, and takes precedence over it. That is, if this property is + * non-null, then the configuration will be ignored. + * + *

If this property isn't set here or in the configuration, the default consistency level will + * be used ({@link #getConsistencyLevel()} or {@code basic.request.consistency}). + */ + @Nullable + ConsistencyLevel getReadConsistencyLevel(); + + /** + * Sets the read consistency level. + * + *

All the driver's built-in implementations are immutable, and return a new instance from this + * method. However custom implementations may choose to be mutable and return the same instance. + * + * @see #getReadConsistencyLevel() + */ + @NonNull + @CheckReturnValue + SelfT setReadConsistencyLevel(@Nullable ConsistencyLevel newReadConsistencyLevel); + + /** + * The consistency level to use for the internal write queries that will be produced by this + * statement. + * + *

This is the programmatic equivalent of the configuration option {@code + * basic.graph.write-consistency-level}, and takes precedence over it. That is, if this property + * is non-null, then the configuration will be ignored. + * + *

If this property isn't set here or in the configuration, the default consistency level will + * be used ({@link #getConsistencyLevel()} or {@code basic.request.consistency}). + */ + @Nullable + ConsistencyLevel getWriteConsistencyLevel(); + + /** + * Sets the write consistency level. + * + *

All the driver's built-in implementations are immutable, and return a new instance from this + * method. However custom implementations may choose to be mutable and return the same instance. + * + * @see #getWriteConsistencyLevel() + */ + @NonNull + @CheckReturnValue + SelfT setWriteConsistencyLevel(@Nullable ConsistencyLevel newWriteConsistencyLevel); + + /** Graph statements do not have a per-query keyspace, this method always returns {@code null}. */ + @Nullable + @Override + default CqlIdentifier getKeyspace() { + return null; + } + + /** Graph statements can't be routed, this method always returns {@code null}. */ + @Nullable + @Override + default CqlIdentifier getRoutingKeyspace() { + return null; + } + + /** Graph statements can't be routed, this method always returns {@code null}. */ + @Nullable + @Override + default ByteBuffer getRoutingKey() { + return null; + } + + /** Graph statements can't be routed, this method always returns {@code null}. */ + @Nullable + @Override + default Token getRoutingToken() { + return null; + } + + /** + * Whether tracing information should be recorded for this statement. + * + *

This method is only exposed for future extensibility. At the time of writing, graph + * statements do not support tracing, and this always returns {@code false}. + */ + default boolean isTracing() { + return false; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/graph/GraphStatementBuilderBase.java b/core/src/main/java/com/datastax/dse/driver/api/core/graph/GraphStatementBuilderBase.java new file mode 100644 index 00000000000..5cb48613cf5 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/graph/GraphStatementBuilderBase.java @@ -0,0 +1,190 @@ +/* + * 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 com.datastax.dse.driver.api.core.graph; + +import com.datastax.oss.driver.api.core.ConsistencyLevel; +import com.datastax.oss.driver.api.core.config.DriverExecutionProfile; +import com.datastax.oss.driver.api.core.cql.Statement; +import com.datastax.oss.driver.api.core.metadata.Node; +import com.datastax.oss.protocol.internal.util.collection.NullAllowingImmutableMap; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.nio.ByteBuffer; +import java.time.Duration; +import java.util.Map; +import net.jcip.annotations.NotThreadSafe; + +@NotThreadSafe +public abstract class GraphStatementBuilderBase< + SelfT extends GraphStatementBuilderBase, + StatementT extends GraphStatement> { + + @SuppressWarnings({"unchecked"}) + private final SelfT self = (SelfT) this; + + protected Boolean isIdempotent; + protected Duration timeout; + protected Node node; + protected long timestamp = Statement.NO_DEFAULT_TIMESTAMP; + protected DriverExecutionProfile executionProfile; + protected String executionProfileName; + private NullAllowingImmutableMap.Builder customPayloadBuilder; + protected String graphName; + protected String traversalSource; + protected String subProtocol; + protected ConsistencyLevel consistencyLevel; + protected ConsistencyLevel readConsistencyLevel; + protected ConsistencyLevel writeConsistencyLevel; + + protected GraphStatementBuilderBase() { + // nothing to do + } + + protected GraphStatementBuilderBase(StatementT template) { + this.isIdempotent = template.isIdempotent(); + this.timeout = template.getTimeout(); + this.node = template.getNode(); + this.timestamp = template.getTimestamp(); + this.executionProfile = template.getExecutionProfile(); + this.executionProfileName = template.getExecutionProfileName(); + if (!template.getCustomPayload().isEmpty()) { + this.customPayloadBuilder = + NullAllowingImmutableMap.builder() + .putAll(template.getCustomPayload()); + } + this.graphName = template.getGraphName(); + this.traversalSource = template.getTraversalSource(); + this.subProtocol = template.getSubProtocol(); + this.consistencyLevel = template.getConsistencyLevel(); + this.readConsistencyLevel = template.getReadConsistencyLevel(); + this.writeConsistencyLevel = template.getWriteConsistencyLevel(); + } + + /** @see GraphStatement#setIdempotent(Boolean) */ + @NonNull + public SelfT setIdempotence(@Nullable Boolean idempotent) { + this.isIdempotent = idempotent; + return self; + } + + /** @see GraphStatement#setTimeout(Duration) */ + @NonNull + public SelfT setTimeout(@Nullable Duration timeout) { + this.timeout = timeout; + return self; + } + + /** @see GraphStatement#setNode(Node) */ + @NonNull + public SelfT setNode(@Nullable Node node) { + this.node = node; + return self; + } + + /** @see GraphStatement#setTimestamp(long) */ + @NonNull + public SelfT setTimestamp(long timestamp) { + this.timestamp = timestamp; + return self; + } + + /** @see GraphStatement#setExecutionProfileName(String) */ + @NonNull + public SelfT setExecutionProfileName(@Nullable String executionProfileName) { + this.executionProfileName = executionProfileName; + return self; + } + + /** @see GraphStatement#setExecutionProfile(DriverExecutionProfile) */ + @NonNull + public SelfT setExecutionProfile(@Nullable DriverExecutionProfile executionProfile) { + this.executionProfile = executionProfile; + this.executionProfileName = null; + return self; + } + + /** @see GraphStatement#setCustomPayload(Map) */ + @NonNull + public SelfT addCustomPayload(@NonNull String key, @Nullable ByteBuffer value) { + if (customPayloadBuilder == null) { + customPayloadBuilder = NullAllowingImmutableMap.builder(); + } + customPayloadBuilder.put(key, value); + return self; + } + + /** @see GraphStatement#setCustomPayload(Map) */ + @NonNull + public SelfT clearCustomPayload() { + customPayloadBuilder = null; + return self; + } + + /** @see GraphStatement#setGraphName(String) */ + @NonNull + public SelfT setGraphName(@Nullable String graphName) { + this.graphName = graphName; + return self; + } + + /** @see GraphStatement#setTraversalSource(String) */ + @NonNull + public SelfT setTraversalSource(@Nullable String traversalSource) { + this.traversalSource = traversalSource; + return self; + } + + /** @see GraphStatement#setSubProtocol(String) */ + @NonNull + public SelfT setSubProtocol(@Nullable String subProtocol) { + this.subProtocol = subProtocol; + return self; + } + + /** @see GraphStatement#setConsistencyLevel(ConsistencyLevel) */ + @NonNull + public SelfT setConsistencyLevel(@Nullable ConsistencyLevel consistencyLevel) { + this.consistencyLevel = consistencyLevel; + return self; + } + + /** @see GraphStatement#setReadConsistencyLevel(ConsistencyLevel) */ + @NonNull + public SelfT setReadConsistencyLevel(@Nullable ConsistencyLevel readConsistencyLevel) { + this.readConsistencyLevel = readConsistencyLevel; + return self; + } + + /** @see GraphStatement#setWriteConsistencyLevel(ConsistencyLevel) */ + @NonNull + public SelfT setWriteConsistencyLevel(@Nullable ConsistencyLevel writeConsistencyLevel) { + this.writeConsistencyLevel = writeConsistencyLevel; + return self; + } + + @NonNull + protected Map buildCustomPayload() { + return (customPayloadBuilder == null) + ? NullAllowingImmutableMap.of() + : customPayloadBuilder.build(); + } + + /** Create the statement with the configuration defined by this builder object. */ + @NonNull + public abstract StatementT build(); +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/graph/PagingEnabledOptions.java b/core/src/main/java/com/datastax/dse/driver/api/core/graph/PagingEnabledOptions.java new file mode 100644 index 00000000000..f59d0e50e93 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/graph/PagingEnabledOptions.java @@ -0,0 +1,24 @@ +/* + * 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 com.datastax.dse.driver.api.core.graph; + +public enum PagingEnabledOptions { + ENABLED, + DISABLED, + AUTO +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/graph/ScriptGraphStatement.java b/core/src/main/java/com/datastax/dse/driver/api/core/graph/ScriptGraphStatement.java new file mode 100644 index 00000000000..2ad7aafc232 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/graph/ScriptGraphStatement.java @@ -0,0 +1,171 @@ +/* + * 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 com.datastax.dse.driver.api.core.graph; + +import com.datastax.dse.driver.internal.core.graph.DefaultScriptGraphStatement; +import com.datastax.oss.driver.api.core.cql.Statement; +import com.datastax.oss.protocol.internal.util.collection.NullAllowingImmutableMap; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.util.Collections; +import java.util.Map; + +/** + * A graph statement that uses a Gremlin-groovy script the query. + * + *

These statements are generally used for DSE Graph set-up queries, such as creating or dropping + * a graph, or defining a graph schema. For graph traversals, we recommend using {@link + * FluentGraphStatement} instead. To do bulk data ingestion in graph, we recommend using {@link + * BatchGraphStatement} instead. + * + *

Typical usage: + * + *

{@code
+ * ScriptGraphStatement statement = ScriptGraphStatement.newInstance("schema.propertyKey('age').Int().create()");
+ *
+ * GraphResultSet graphResultSet = dseSession.execute(statement);
+ * }
+ */ +public interface ScriptGraphStatement extends GraphStatement { + + /** Create a new instance from the given script. */ + @NonNull + static ScriptGraphStatement newInstance(@NonNull String script) { + return new DefaultScriptGraphStatement( + script, + NullAllowingImmutableMap.of(), + null, + null, + null, + null, + Statement.NO_DEFAULT_TIMESTAMP, + null, + null, + Collections.emptyMap(), + null, + null, + null, + null, + null, + null); + } + + /** + * Create a builder object to start creating a new instance from the given script. + * + *

Note that this builder is mutable and not thread-safe. + */ + @NonNull + static ScriptGraphStatementBuilder builder(@NonNull String script) { + return new ScriptGraphStatementBuilder(script); + } + + /** + * Create a builder helper object to start creating a new instance with an existing statement as a + * template. The script and options set on the template will be copied for the new statement at + * the moment this method is called. + * + *

Note that this builder is mutable and not thread-safe. + */ + @NonNull + static ScriptGraphStatementBuilder builder(@NonNull ScriptGraphStatement template) { + return new ScriptGraphStatementBuilder(template); + } + + /** The Gremlin-groovy script representing the graph query. */ + @NonNull + String getScript(); + + /** + * Whether the statement is a system query, or {@code null} if it defaults to the value defined in + * the configuration. + * + * @see #setSystemQuery(Boolean) + */ + @Nullable + Boolean isSystemQuery(); + + /** + * Defines if this statement is a system query. + * + *

Script statements that access the {@code system} variable must not specify a graph + * name (otherwise {@code system} is not available). However, if your application executes a lot + * of non-system statements, it is convenient to configure the graph name in your configuration to + * avoid repeating it every time. This method allows you to ignore that global graph name for a + * specific statement. + * + *

This property is the programmatic equivalent of the configuration option {@code + * basic.graph.is-system-query}, and takes precedence over it. That is, if this property is + * non-null, then the configuration will be ignored. + * + *

The driver's built-in implementation is immutable, and returns a new instance from this + * method. However custom implementations may choose to be mutable and return the same instance. + * + * @param newValue {@code true} to mark this statement as a system query (the driver will ignore + * any graph name set on the statement or the configuration); {@code false} to mark it as a + * non-system query; {@code null} to default to the value defined in the configuration. + * @see #isSystemQuery() + */ + @NonNull + ScriptGraphStatement setSystemQuery(@Nullable Boolean newValue); + + /** + * The query parameters to send along the request. + * + * @see #setQueryParam(String, Object) + */ + @NonNull + Map getQueryParams(); + + /** + * Set a value for a parameter defined in the Groovy script. + * + *

The script engine in the DSE Graph server allows to define parameters in a Groovy script and + * set the values of these parameters as a binding. Defining parameters allows to re-use scripts + * and only change their parameters values, which improves the performance of the script executed, + * so defining parameters is encouraged; however, for optimal Graph traversal performance, we + * recommend either using {@link BatchGraphStatement}s for data ingestion, or {@link + * FluentGraphStatement} for normal traversals. + * + *

Parameters in a Groovy script are always named; unlike CQL, they are not prefixed by a + * column ({@code :}). + * + *

The driver's built-in implementation is immutable, and returns a new instance from this + * method. However custom implementations may choose to be mutable and return the same instance. + * If many parameters are to be set in a query, it is recommended to create the statement with + * {@link #builder(String)} instead. + * + * @param name the name of the parameter defined in the script. If the statement already had a + * binding for this name, it gets replaced. + * @param value the value that will be transmitted with the request. + */ + @NonNull + ScriptGraphStatement setQueryParam(@NonNull String name, @Nullable Object value); + + /** + * Removes a binding for the given name from this statement. + * + *

If the statement did not have such a binding, this method has no effect and returns the same + * statement instance. Otherwise, the driver's built-in implementation returns a new instance + * (however custom implementations may choose to be mutable and return the same instance). + * + * @see #setQueryParam(String, Object) + */ + @NonNull + ScriptGraphStatement removeQueryParam(@NonNull String name); +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/graph/ScriptGraphStatementBuilder.java b/core/src/main/java/com/datastax/dse/driver/api/core/graph/ScriptGraphStatementBuilder.java new file mode 100644 index 00000000000..1985c58955f --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/graph/ScriptGraphStatementBuilder.java @@ -0,0 +1,136 @@ +/* + * 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 com.datastax.dse.driver.api.core.graph; + +import com.datastax.dse.driver.internal.core.graph.DefaultScriptGraphStatement; +import com.datastax.oss.driver.shaded.guava.common.base.Preconditions; +import com.datastax.oss.driver.shaded.guava.common.collect.Maps; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.util.Map; +import net.jcip.annotations.NotThreadSafe; + +/** + * A builder to create a script graph statement. + * + *

This class is mutable and not thread-safe. + */ +@NotThreadSafe +public class ScriptGraphStatementBuilder + extends GraphStatementBuilderBase { + + private String script; + private Boolean isSystemQuery; + private final Map queryParams; + + public ScriptGraphStatementBuilder() { + this.queryParams = Maps.newHashMap(); + } + + public ScriptGraphStatementBuilder(String script) { + this.script = script; + this.queryParams = Maps.newHashMap(); + } + + public ScriptGraphStatementBuilder(ScriptGraphStatement template) { + super(template); + this.script = template.getScript(); + this.queryParams = Maps.newHashMap(template.getQueryParams()); + this.isSystemQuery = template.isSystemQuery(); + } + + @NonNull + public ScriptGraphStatementBuilder setScript(@NonNull String script) { + this.script = script; + return this; + } + + /** @see ScriptGraphStatement#isSystemQuery() */ + @NonNull + public ScriptGraphStatementBuilder setSystemQuery(@Nullable Boolean isSystemQuery) { + this.isSystemQuery = isSystemQuery; + return this; + } + + /** + * Set a value for a parameter defined in the script query. + * + * @see ScriptGraphStatement#setQueryParam(String, Object) + */ + @NonNull + public ScriptGraphStatementBuilder setQueryParam(@NonNull String name, @Nullable Object value) { + this.queryParams.put(name, value); + return this; + } + + /** + * Set multiple values for named parameters defined in the script query. + * + * @see ScriptGraphStatement#setQueryParam(String, Object) + */ + @NonNull + public ScriptGraphStatementBuilder setQueryParams(@NonNull Map params) { + this.queryParams.putAll(params); + return this; + } + + /** + * Removes a parameter. + * + *

This is useful if the builder was {@linkplain + * ScriptGraphStatement#builder(ScriptGraphStatement) initialized with a template statement} that + * has more parameters than desired. + * + * @see ScriptGraphStatement#setQueryParam(String, Object) + * @see #clearQueryParams() + */ + @NonNull + public ScriptGraphStatementBuilder removeQueryParam(@NonNull String name) { + this.queryParams.remove(name); + return this; + } + + /** Clears all the parameters previously added to this builder. */ + public ScriptGraphStatementBuilder clearQueryParams() { + this.queryParams.clear(); + return this; + } + + @NonNull + @Override + public ScriptGraphStatement build() { + Preconditions.checkNotNull(this.script, "Script hasn't been defined in this builder."); + return new DefaultScriptGraphStatement( + this.script, + this.queryParams, + this.isSystemQuery, + isIdempotent, + timeout, + node, + timestamp, + executionProfile, + executionProfileName, + buildCustomPayload(), + graphName, + traversalSource, + subProtocol, + consistencyLevel, + readConsistencyLevel, + writeConsistencyLevel); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/graph/predicates/CqlCollection.java b/core/src/main/java/com/datastax/dse/driver/api/core/graph/predicates/CqlCollection.java new file mode 100644 index 00000000000..fdbf3fbe397 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/graph/predicates/CqlCollection.java @@ -0,0 +1,81 @@ +/* + * 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 com.datastax.dse.driver.api.core.graph.predicates; + +import com.datastax.dse.driver.internal.core.graph.CqlCollectionPredicate; +import java.util.Collection; +import java.util.Map; +import org.apache.tinkerpop.gremlin.process.traversal.P; +import org.javatuples.Pair; + +/** + * Predicates that can be used on CQL collections (lists, sets and maps). + * + *

Note: CQL collection predicates are only available when using the binary subprotocol. + */ +public class CqlCollection { + + /** + * Checks if the target collection contains the given value. + * + * @param value the value to look for; cannot be {@code null}. + * @return a predicate to apply in a {@link + * org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal}. + */ + @SuppressWarnings("unchecked") + public static , V> P contains(V value) { + return new P(CqlCollectionPredicate.contains, value); + } + + /** + * Checks if the target map contains the given key. + * + * @param key the key to look for; cannot be {@code null}. + * @return a predicate to apply in a {@link + * org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal}. + */ + @SuppressWarnings("unchecked") + public static , K> P containsKey(K key) { + return new P(CqlCollectionPredicate.containsKey, key); + } + + /** + * Checks if the target map contains the given value. + * + * @param value the value to look for; cannot be {@code null}. + * @return a predicate to apply in a {@link + * org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal}. + */ + @SuppressWarnings("unchecked") + public static , V> P containsValue(V value) { + return new P(CqlCollectionPredicate.containsValue, value); + } + + /** + * Checks if the target map contains the given entry. + * + * @param key the key to look for; cannot be {@code null}. + * @param value the value to look for; cannot be {@code null}. + * @return a predicate to apply in a {@link + * org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal}. + */ + @SuppressWarnings("unchecked") + public static , K, V> P entryEq(K key, V value) { + return new P(CqlCollectionPredicate.entryEq, new Pair<>(key, value)); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/graph/predicates/Geo.java b/core/src/main/java/com/datastax/dse/driver/api/core/graph/predicates/Geo.java new file mode 100644 index 00000000000..65dd84d0076 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/graph/predicates/Geo.java @@ -0,0 +1,160 @@ +/* + * 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 com.datastax.dse.driver.api.core.graph.predicates; + +import com.datastax.dse.driver.api.core.data.geometry.LineString; +import com.datastax.dse.driver.api.core.data.geometry.Point; +import com.datastax.dse.driver.api.core.data.geometry.Polygon; +import com.datastax.dse.driver.internal.core.data.geometry.Distance; +import com.datastax.dse.driver.internal.core.graph.GeoPredicate; +import com.datastax.dse.driver.internal.core.graph.GeoUtils; +import edu.umd.cs.findbugs.annotations.NonNull; +import org.apache.tinkerpop.gremlin.process.traversal.P; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; + +public interface Geo { + + enum Unit { + MILES(GeoUtils.MILES_TO_KM * GeoUtils.KM_TO_DEG), + KILOMETERS(GeoUtils.KM_TO_DEG), + METERS(GeoUtils.KM_TO_DEG / 1000.0), + DEGREES(1); + + private final double multiplier; + + Unit(double multiplier) { + this.multiplier = multiplier; + } + + /** Convert distance to degrees (used internally only). */ + public double toDegrees(double distance) { + return distance * multiplier; + } + } + + /** + * Finds whether an entity is inside the given circular area using a geo coordinate system. + * + * @return a predicate to apply in a {@link GraphTraversal}. + */ + static P inside(Point center, double radius, Unit units) { + return new P<>(GeoPredicate.inside, new Distance(center, units.toDegrees(radius))); + } + + /** + * Finds whether an entity is inside the given circular area using a cartesian coordinate system. + * + * @return a predicate to apply in a {@link GraphTraversal}. + */ + static P inside(Point center, double radius) { + return new P<>(GeoPredicate.insideCartesian, new Distance(center, radius)); + } + + /** + * Finds whether an entity is inside the given polygon. + * + * @return a predicate to apply in a {@link GraphTraversal}. + */ + static P inside(Polygon polygon) { + return new P<>(GeoPredicate.insideCartesian, polygon); + } + + /** + * Creates a point from the given coordinates. + * + *

This is just a shortcut to {@link Point#fromCoordinates(double, double)}. It is duplicated + * here so that {@code Geo} can be used as a single entry point in Gremlin-groovy scripts. + */ + @NonNull + static Point point(double x, double y) { + return Point.fromCoordinates(x, y); + } + + /** + * Creates a line string from the given (at least 2) points. + * + *

This is just a shortcut to {@link LineString#fromPoints(Point, Point, Point...)}. It is + * duplicated here so that {@code Geo} can be used as a single entry point in Gremlin-groovy + * scripts. + */ + @NonNull + static LineString lineString( + @NonNull Point point1, @NonNull Point point2, @NonNull Point... otherPoints) { + return LineString.fromPoints(point1, point2, otherPoints); + } + + /** + * Creates a line string from the coordinates of its points. + * + *

This is provided for backward compatibility with previous DSE versions. We recommend {@link + * #lineString(Point, Point, Point...)} instead. + */ + @NonNull + static LineString lineString(double... coordinates) { + if (coordinates.length % 2 != 0) { + throw new IllegalArgumentException("lineString() must be passed an even number of arguments"); + } else if (coordinates.length < 4) { + throw new IllegalArgumentException( + "lineString() must be passed at least 4 arguments (2 points)"); + } + Point point1 = Point.fromCoordinates(coordinates[0], coordinates[1]); + Point point2 = Point.fromCoordinates(coordinates[2], coordinates[3]); + Point[] otherPoints = new Point[coordinates.length / 2 - 2]; + for (int i = 4; i < coordinates.length; i += 2) { + otherPoints[i / 2 - 2] = Point.fromCoordinates(coordinates[i], coordinates[i + 1]); + } + return LineString.fromPoints(point1, point2, otherPoints); + } + + /** + * Creates a polygon from the given (at least 3) points. + * + *

This is just a shortcut to {@link Polygon#fromPoints(Point, Point, Point, Point...)}. It is + * duplicated here so that {@code Geo} can be used as a single entry point in Gremlin-groovy + * scripts. + */ + @NonNull + static Polygon polygon( + @NonNull Point p1, @NonNull Point p2, @NonNull Point p3, @NonNull Point... otherPoints) { + return Polygon.fromPoints(p1, p2, p3, otherPoints); + } + + /** + * Creates a polygon from the coordinates of its points. + * + *

This is provided for backward compatibility with previous DSE versions. We recommend {@link + * #polygon(Point, Point, Point, Point...)} instead. + */ + @NonNull + static Polygon polygon(double... coordinates) { + if (coordinates.length % 2 != 0) { + throw new IllegalArgumentException("polygon() must be passed an even number of arguments"); + } else if (coordinates.length < 6) { + throw new IllegalArgumentException( + "polygon() must be passed at least 6 arguments (3 points)"); + } + Point point1 = Point.fromCoordinates(coordinates[0], coordinates[1]); + Point point2 = Point.fromCoordinates(coordinates[2], coordinates[3]); + Point point3 = Point.fromCoordinates(coordinates[4], coordinates[5]); + Point[] otherPoints = new Point[coordinates.length / 2 - 3]; + for (int i = 6; i < coordinates.length; i += 2) { + otherPoints[i / 2 - 3] = Point.fromCoordinates(coordinates[i], coordinates[i + 1]); + } + return Polygon.fromPoints(point1, point2, point3, otherPoints); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/graph/predicates/Search.java b/core/src/main/java/com/datastax/dse/driver/api/core/graph/predicates/Search.java new file mode 100644 index 00000000000..e285c118c8a --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/graph/predicates/Search.java @@ -0,0 +1,136 @@ +/* + * 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 com.datastax.dse.driver.api.core.graph.predicates; + +import com.datastax.dse.driver.internal.core.graph.EditDistance; +import com.datastax.dse.driver.internal.core.graph.SearchPredicate; +import org.apache.tinkerpop.gremlin.process.traversal.P; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; + +public interface Search { + + /** + * Search any instance of a certain token within the text property targeted (case insensitive). + * + * @return a predicate to apply in a {@link GraphTraversal}. + */ + static P token(String value) { + return new P<>(SearchPredicate.token, value); + } + + /** + * Search any instance of a certain token prefix within the text property targeted (case + * insensitive). + * + * @return a predicate to apply in a {@link GraphTraversal}. + */ + static P tokenPrefix(String value) { + return new P<>(SearchPredicate.tokenPrefix, value); + } + + /** + * Search any instance of the provided regular expression for the targeted property (case + * insensitive). + * + * @return a predicate to apply in a {@link GraphTraversal}. + */ + static P tokenRegex(String value) { + return new P<>(SearchPredicate.tokenRegex, value); + } + + /** + * Search for a specific prefix at the beginning of the text property targeted (case sensitive). + * + * @return a predicate to apply in a {@link GraphTraversal}. + */ + static P prefix(String value) { + return new P<>(SearchPredicate.prefix, value); + } + + /** + * Search for this regular expression inside the text property targeted (case sensitive). + * + * @return a predicate to apply in a {@link GraphTraversal}. + */ + static P regex(String value) { + return new P<>(SearchPredicate.regex, value); + } + + /** + * Supports finding words which are a within a specific distance away (case insensitive). + * + *

Example: the search expression is {@code phrase("Hello world", 2)} + * + *

    + *
  • the inserted value "Hello world" is found + *
  • the inserted value "Hello wild world" is found + *
  • the inserted value "Hello big wild world" is found + *
  • the inserted value "Hello the big wild world" is not found + *
  • the inserted value "Goodbye world" is not found. + *
+ * + * @param query the string to look for in the value + * @param distance the number of terms allowed between two correct terms to find a value. + * @return a predicate to apply in a {@link GraphTraversal}. + */ + static P phrase(String query, int distance) { + return new P<>(SearchPredicate.phrase, new EditDistance(query, distance)); + } + + /** + * Supports fuzzy searches based on the Damerau-Levenshtein Distance, or Edit Distance algorithm + * (case sensitive). + * + *

Example: the search expression is {@code fuzzy("david", 1)} + * + *

    + *
  • the inserted value "david" is found + *
  • the inserted value "dawid" is found + *
  • the inserted value "davids" is found + *
  • the inserted value "dewid" is not found + *
+ * + * @param query the string to look for in the value + * @param distance the number of "uncertainties" allowed for the Levenshtein algorithm. + * @return a predicate to apply in a {@link GraphTraversal}. + */ + static P fuzzy(String query, int distance) { + return new P<>(SearchPredicate.fuzzy, new EditDistance(query, distance)); + } + + /** + * Supports fuzzy searches based on the Damerau-Levenshtein Distance, or Edit Distance algorithm + * after having tokenized the data stored (case insensitive). + * + *

Example: the search expression is {@code tokenFuzzy("david", 1)} + * + *

    + *
  • the inserted value "david" is found + *
  • the inserted value "dawid" is found + *
  • the inserted value "hello-dawid" is found + *
  • the inserted value "dewid" is not found + *
+ * + * @param query the string to look for in the value + * @param distance the number of "uncertainties" allowed for the Levenshtein algorithm. + * @return a predicate to apply in a {@link GraphTraversal}. + */ + static P tokenFuzzy(String query, int distance) { + return new P<>(SearchPredicate.tokenFuzzy, new EditDistance(query, distance)); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/graph/reactive/ReactiveGraphNode.java b/core/src/main/java/com/datastax/dse/driver/api/core/graph/reactive/ReactiveGraphNode.java new file mode 100644 index 00000000000..ad7849633c6 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/graph/reactive/ReactiveGraphNode.java @@ -0,0 +1,47 @@ +/* + * 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 com.datastax.dse.driver.api.core.graph.reactive; + +import com.datastax.dse.driver.api.core.graph.GraphNode; +import com.datastax.oss.driver.api.core.cql.ExecutionInfo; +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * A {@link GraphNode} produced by a {@linkplain ReactiveGraphResultSet reactive graph result set}. + * + *

This is essentially an extension of the driver's {@link GraphNode} object that also exposes + * useful information about {@linkplain #getExecutionInfo() request execution} (note however that + * this information is also exposed at result set level for convenience). + * + * @see ReactiveGraphSession + * @see ReactiveGraphResultSet + */ +public interface ReactiveGraphNode extends GraphNode { + + /** + * The execution information for the paged request that produced this result. + * + *

This object is the same for two rows pertaining to the same page, but differs for rows + * pertaining to different pages. + * + * @return the execution information for the paged request that produced this result. + * @see ReactiveGraphResultSet#getExecutionInfos() + */ + @NonNull + ExecutionInfo getExecutionInfo(); +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/graph/reactive/ReactiveGraphResultSet.java b/core/src/main/java/com/datastax/dse/driver/api/core/graph/reactive/ReactiveGraphResultSet.java new file mode 100644 index 00000000000..a0e3231750e --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/graph/reactive/ReactiveGraphResultSet.java @@ -0,0 +1,66 @@ +/* + * 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 com.datastax.dse.driver.api.core.graph.reactive; + +import com.datastax.dse.driver.api.core.graph.GraphStatement; +import com.datastax.oss.driver.api.core.cql.ExecutionInfo; +import edu.umd.cs.findbugs.annotations.NonNull; +import org.reactivestreams.Publisher; + +/** + * A {@link Publisher} of {@link ReactiveGraphNode}s returned by a {@link ReactiveGraphSession}. + * + *

By default, all implementations returned by the driver are cold, unicast, single-subscriber + * only publishers. In other words, they do not support multiple subscriptions; consider + * caching the results produced by such publishers if you need to consume them by more than one + * downstream subscriber. + * + *

Also, note that reactive graph result sets may emit items to their subscribers on an internal + * driver IO thread. Subscriber implementors are encouraged to abide by Reactive Streams + * Specification rule 2.2 and avoid performing heavy computations or blocking calls inside + * {@link org.reactivestreams.Subscriber#onNext(Object) onNext} calls, as doing so could slow down + * the driver and impact performance. Instead, they should asynchronously dispatch received signals + * to their processing logic. + * + *

This interface exists mainly to expose useful information about {@linkplain + * #getExecutionInfos() request execution}. This is particularly convenient for queries that do not + * return rows; for queries that do return rows, it is also possible, and oftentimes easier, to + * access that same information {@linkplain ReactiveGraphNode at node level}. + * + * @see ReactiveGraphSession#executeReactive(GraphStatement) + * @see ReactiveGraphNode + */ +public interface ReactiveGraphResultSet extends Publisher { + + /** + * Returns {@linkplain ExecutionInfo information about the execution} of all requests that have + * been performed so far to assemble this result set. + * + *

If the query is not paged, this publisher will emit exactly one item as soon as the response + * arrives, then complete. If the query is paged, it will emit multiple items, one per page; then + * it will complete when the last page arrives. If the query execution fails, then this publisher + * will fail with the same error. + * + *

By default, publishers returned by this method do not support multiple subscriptions. + * + * @see ReactiveGraphNode#getExecutionInfo() + */ + @NonNull + Publisher getExecutionInfos(); +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/graph/reactive/ReactiveGraphSession.java b/core/src/main/java/com/datastax/dse/driver/api/core/graph/reactive/ReactiveGraphSession.java new file mode 100644 index 00000000000..88f0e5def61 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/graph/reactive/ReactiveGraphSession.java @@ -0,0 +1,49 @@ +/* + * 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 com.datastax.dse.driver.api.core.graph.reactive; + +import com.datastax.dse.driver.api.core.graph.GraphStatement; +import com.datastax.dse.driver.internal.core.graph.reactive.ReactiveGraphRequestProcessor; +import com.datastax.oss.driver.api.core.session.Session; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.Objects; + +/** + * A {@link Session} that offers utility methods to issue graph queries using reactive-style + * programming. + */ +public interface ReactiveGraphSession extends Session { + + /** + * Returns a {@link ReactiveGraphResultSet} that, once subscribed to, executes the given query and + * emits all the results. + * + *

See the javadocs of {@link ReactiveGraphResultSet} for important remarks anc caveats + * regarding the subscription to and consumption of reactive graph result sets. + * + * @param statement the statement to execute. + * @return The {@link ReactiveGraphResultSet} that will publish the returned results. + * @see ReactiveGraphResultSet + * @see ReactiveGraphNode + */ + @NonNull + default ReactiveGraphResultSet executeReactive(@NonNull GraphStatement statement) { + return Objects.requireNonNull( + execute(statement, ReactiveGraphRequestProcessor.REACTIVE_GRAPH_RESULT_SET)); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/metadata/DseNodeProperties.java b/core/src/main/java/com/datastax/dse/driver/api/core/metadata/DseNodeProperties.java new file mode 100644 index 00000000000..88dbc164588 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/metadata/DseNodeProperties.java @@ -0,0 +1,137 @@ +/* + * 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 com.datastax.dse.driver.api.core.metadata; + +import com.datastax.oss.driver.api.core.Version; +import com.datastax.oss.driver.api.core.metadata.Node; + +/** The keys for the additional DSE-specific properties stored in {@link Node#getExtras()}. */ +public class DseNodeProperties { + + /** + * The DSE version that the node is running. + * + *

The associated value in {@link Node#getExtras()} is a {@link Version}). + */ + public static final String DSE_VERSION = "DSE_VERSION"; + + /** + * The value of the {@code server_id} field in the {@code peers} system table for this node. + * + *

This is the single identifier of the machine running a DSE instance. If DSE has been + * configured with Multi-Instance, the {@code server_id} helps identifying the single physical + * machine that runs the multiple DSE instances. If DSE is not configured with DSE Multi-Instance, + * the {@code server_id} will be automatically set and be unique for each node. + * + *

This information is only available if connecting to a DSE 6.0+ node. + * + *

The associated value in {@link Node#getExtras()} is a {@code String}). + * + * @see DSE + * Multi-Instance (DSE Administrator Guide) + * @see + * server_id (DSE Administrator Guide) + */ + public static final String SERVER_ID = "SERVER_ID"; + + /** + * The DSE workloads that the node is running. + * + *

This is based on the {@code workload} or {@code workloads} columns in {@code system.local} + * and {@code system.peers}. + * + *

Workload labels may vary depending on the DSE version in use; e.g. DSE 5.1 may report two + * distinct workloads: {@code Search} and {@code Analytics}, while DSE 5.0 would report a single + * {@code SearchAnalytics} workload instead. It is up to users to deal with such discrepancies; + * the driver simply returns the workload labels as reported by DSE, without any form of + * pre-processing (with the exception of Graph in DSE 5.0, which is stored in a separate column, + * but will be reported as {@code Graph} here). + * + *

The associated value in {@link Node#getExtras()} is an immutable {@code Set}. + */ + public static final String DSE_WORKLOADS = "DSE_WORKLOADS"; + + /** + * The port for the native transport connections on the DSE node. + * + *

The native transport port is {@code 9042} by default but can be changed on instances + * requiring specific firewall configurations. This can be configured in the {@code + * cassandra.yaml} configuration file under the {@code native_transport_port} property. + * + *

This information is only available if connecting the driver to a DSE 6.0+ node. + * + *

The associated value in {@link Node#getExtras()} is an {@code Integer}. + */ + public static final String NATIVE_TRANSPORT_PORT = "NATIVE_TRANSPORT_PORT"; + + /** + * The port for the encrypted native transport connections on the DSE node. + * + *

In most scenarios enabling client communications in DSE will result in using a single port + * that will only accept encrypted connections (by default the port {@code 9042} is reused since + * unencrypted connections are not allowed). + * + *

However, it is possible to configure DSE to use both encrypted and a non-encrypted + * communication ports with clients. In that case the port accepting encrypted connections will + * differ from the non-encrypted one (see {@link #NATIVE_TRANSPORT_PORT}) and will be exposed via + * this method. + * + *

This information is only available if connecting the driver to a DSE 6.0+ node. + * + *

The associated value in {@link Node#getExtras()} is an {@code Integer}. + */ + public static final String NATIVE_TRANSPORT_PORT_SSL = "NATIVE_TRANSPORT_PORT_SSL"; + + /** + * The storage port used by the DSE node. + * + *

The storage port is used for internal communication between the DSE server nodes. This port + * is never used by the driver. + * + *

This information is only available if connecting the driver to a DSE 6.0+ node. + * + *

The associated value in {@link Node#getExtras()} is an {@code Integer}. + */ + public static final String STORAGE_PORT = "STORAGE_PORT"; + + /** + * The encrypted storage port used by the DSE node. + * + *

If inter-node encryption is enabled on the DSE cluster, nodes will communicate securely + * between each other via this port. This port is never used by the driver. + * + *

This information is only available if connecting the driver to a DSE 6.0+ node. + * + *

The associated value in {@link Node#getExtras()} is an {@code Integer}. + */ + public static final String STORAGE_PORT_SSL = "STORAGE_PORT_SSL"; + + /** + * The JMX port used by this node. + * + *

The JMX port can be configured in the {@code cassandra-env.sh} configuration file separately + * on each node. + * + *

This information is only available if connecting the driver to a DSE 6.0+ node. + * + *

The associated value in {@link Node#getExtras()} is an {@code Integer}. + */ + public static final String JMX_PORT = "JMX_PORT"; +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/metadata/schema/DseAggregateMetadata.java b/core/src/main/java/com/datastax/dse/driver/api/core/metadata/schema/DseAggregateMetadata.java new file mode 100644 index 00000000000..609c64f7c15 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/metadata/schema/DseAggregateMetadata.java @@ -0,0 +1,97 @@ +/* + * 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 com.datastax.dse.driver.api.core.metadata.schema; + +import com.datastax.oss.driver.api.core.metadata.schema.AggregateMetadata; +import com.datastax.oss.driver.api.core.type.DataType; +import com.datastax.oss.driver.internal.core.metadata.schema.ScriptBuilder; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.Optional; + +/** + * Specialized aggregate metadata for DSE. + * + *

It adds support for the DSE-specific {@link #getDeterministic() DETERMINISTIC} keyword. + */ +public interface DseAggregateMetadata extends AggregateMetadata { + + /** @deprecated Use {@link #getDeterministic()} instead. */ + @Deprecated + boolean isDeterministic(); + + /** + * Indicates if this aggregate is deterministic. A deterministic aggregate means that given a + * particular input, the aggregate will always produce the same output. + * + *

This method returns {@linkplain Optional#empty() empty} if this information was not found in + * the system tables, regardless of the actual aggregate characteristics; this is the case for all + * versions of DSE older than 6.0.0. + * + * @return Whether or not this aggregate is deterministic; or {@linkplain Optional#empty() empty} + * if such information is not available in the system tables. + */ + default Optional getDeterministic() { + return Optional.of(isDeterministic()); + } + + @NonNull + @Override + default String describe(boolean pretty) { + // Easiest to just copy the OSS describe() method and add in DETERMINISTIC + ScriptBuilder builder = new ScriptBuilder(pretty); + builder + .append("CREATE AGGREGATE ") + .append(getKeyspace()) + .append(".") + .append(getSignature().getName()) + .append("("); + boolean first = true; + for (int i = 0; i < getSignature().getParameterTypes().size(); i++) { + if (first) { + first = false; + } else { + builder.append(","); + } + DataType type = getSignature().getParameterTypes().get(i); + builder.append(type.asCql(false, pretty)); + } + builder + .increaseIndent() + .append(")") + .newLine() + .append("SFUNC ") + .append(getStateFuncSignature().getName()) + .newLine() + .append("STYPE ") + .append(getStateType().asCql(false, pretty)); + + if (getFinalFuncSignature().isPresent()) { + builder.newLine().append("FINALFUNC ").append(getFinalFuncSignature().get().getName()); + } + if (getInitCond().isPresent()) { + Optional formatInitCond = formatInitCond(); + assert formatInitCond.isPresent(); + builder.newLine().append("INITCOND ").append(formatInitCond.get()); + } + // add DETERMINISTIC if present + if (getDeterministic().orElse(false)) { + builder.newLine().append("DETERMINISTIC"); + } + return builder.append(";").build(); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/metadata/schema/DseColumnMetadata.java b/core/src/main/java/com/datastax/dse/driver/api/core/metadata/schema/DseColumnMetadata.java new file mode 100644 index 00000000000..62b5650697e --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/metadata/schema/DseColumnMetadata.java @@ -0,0 +1,28 @@ +/* + * 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 com.datastax.dse.driver.api.core.metadata.schema; + +import com.datastax.oss.driver.api.core.metadata.schema.ColumnMetadata; + +/** + * Specialized column metadata for DSE. + * + *

This type exists only for future extensibility; currently, it is identical to {@link + * ColumnMetadata}. + */ +public interface DseColumnMetadata extends ColumnMetadata {} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/metadata/schema/DseEdgeMetadata.java b/core/src/main/java/com/datastax/dse/driver/api/core/metadata/schema/DseEdgeMetadata.java new file mode 100644 index 00000000000..59ee8a277ff --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/metadata/schema/DseEdgeMetadata.java @@ -0,0 +1,62 @@ +/* + * 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 com.datastax.dse.driver.api.core.metadata.schema; + +import com.datastax.oss.driver.api.core.CqlIdentifier; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.List; + +/** Edge metadata, for a table that was created with CREATE TABLE ... WITH EDGE LABEL. */ +public interface DseEdgeMetadata { + + /** The label of the edge in graph. */ + @NonNull + CqlIdentifier getLabelName(); + + /** The identifier of the table representing the incoming vertex. */ + @NonNull + CqlIdentifier getFromTable(); + + /** The label of the incoming vertex in graph. */ + @NonNull + CqlIdentifier getFromLabel(); + + /** The columns in this table that match the partition key of the incoming vertex table. */ + @NonNull + List getFromPartitionKeyColumns(); + + /** The columns in this table that match the clustering columns of the incoming vertex table. */ + @NonNull + List getFromClusteringColumns(); + + /** The identifier of the table representing the outgoing vertex. */ + @NonNull + CqlIdentifier getToTable(); + + /** The label of the outgoing vertex in graph. */ + @NonNull + CqlIdentifier getToLabel(); + + /** The columns in this table that match the partition key of the outgoing vertex table. */ + @NonNull + List getToPartitionKeyColumns(); + + /** The columns in this table that match the clustering columns of the outgoing vertex table. */ + @NonNull + List getToClusteringColumns(); +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/metadata/schema/DseFunctionMetadata.java b/core/src/main/java/com/datastax/dse/driver/api/core/metadata/schema/DseFunctionMetadata.java new file mode 100644 index 00000000000..91298795959 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/metadata/schema/DseFunctionMetadata.java @@ -0,0 +1,184 @@ +/* + * 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 com.datastax.dse.driver.api.core.metadata.schema; + +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.api.core.metadata.schema.FunctionMetadata; +import com.datastax.oss.driver.api.core.type.DataType; +import com.datastax.oss.driver.internal.core.metadata.schema.ScriptBuilder; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.List; +import java.util.Optional; + +/** + * Specialized function metadata for DSE. + * + *

It adds support for the DSE-specific {@link #getDeterministic() DETERMINISTIC} and {@link + * #getMonotonicity() MONOTONIC} keywords. + */ +public interface DseFunctionMetadata extends FunctionMetadata { + + /** The monotonicity of a function. */ + enum Monotonicity { + + /** + * Indicates that the function is fully monotonic on all of its arguments. This means that it is + * either entirely non-increasing or non-decreasing. Full monotonicity is required to use the + * function in a GROUP BY clause. + */ + FULLY_MONOTONIC, + + /** + * Indicates that the function is partially monotonic, meaning that partial application over + * some of the its arguments is monotonic. Currently (DSE 6.0.0), CQL only allows partial + * monotonicity on exactly one argument. This may change in a future CQL version. + */ + PARTIALLY_MONOTONIC, + + /** Indicates that the function is not monotonic. */ + NOT_MONOTONIC, + } + + /** @deprecated Use {@link #getDeterministic()} instead. */ + @Deprecated + boolean isDeterministic(); + + /** + * Indicates if this function is deterministic. A deterministic function means that given a + * particular input, the function will always produce the same output. + * + *

This method returns {@linkplain Optional#empty() empty} if this information was not found in + * the system tables, regardless of the actual function characteristics; this is the case for all + * versions of DSE older than 6.0.0. + * + * @return Whether or not this function is deterministic; or {@linkplain Optional#empty() empty} + * if such information is not available in the system tables. + */ + default Optional getDeterministic() { + return Optional.of(isDeterministic()); + } + + /** @deprecated use {@link #getMonotonicity()} instead. */ + @Deprecated + boolean isMonotonic(); + + /** + * Returns this function's {@link Monotonicity}. + * + *

A function can be either: + * + *

    + *
  • fully monotonic. In that case, this method returns {@link Monotonicity#FULLY_MONOTONIC}, + * and {@link #getMonotonicArgumentNames()} returns all the arguments; + *
  • partially monotonic, meaning that partial application over some of the arguments is + * monotonic. Currently (DSE 6.0.0), CQL only allows partial monotonicity on exactly one + * argument. This may change in a future CQL version. In that case, this method returns + * {@link Monotonicity#PARTIALLY_MONOTONIC}, and {@link #getMonotonicArgumentNames()} + * returns a singleton list; + *
  • not monotonic. In that case, this method return {@link Monotonicity#NOT_MONOTONIC} and + * {@link #getMonotonicArgumentNames()} returns an empty list. + *
+ * + *

Full monotonicity is required to use the function in a GROUP BY clause. + * + *

This method returns {@linkplain Optional#empty() empty} if this information was not found in + * the system tables, regardless of the actual function characteristics; this is the case for all + * versions of DSE older than 6.0.0. + * + * @return this function's {@link Monotonicity}; or {@linkplain Optional#empty() empty} if such + * information is not available in the system tables. + */ + default Optional getMonotonicity() { + return Optional.of( + isMonotonic() + ? Monotonicity.FULLY_MONOTONIC + : getMonotonicArgumentNames().isEmpty() + ? Monotonicity.NOT_MONOTONIC + : Monotonicity.PARTIALLY_MONOTONIC); + } + + /** + * Returns a list of argument names that are monotonic. + * + *

See {@link #getMonotonicity()} for explanations on monotonicity, and the possible values + * returned by this method. + * + *

NOTE: For versions of DSE older than 6.0.0, this method will always return an empty list, + * regardless of the actual function characteristics. + * + * @return the argument names that the function is monotonic on. + */ + @NonNull + List getMonotonicArgumentNames(); + + @NonNull + @Override + default String describe(boolean pretty) { + ScriptBuilder builder = new ScriptBuilder(pretty); + builder + .append("CREATE FUNCTION ") + .append(getKeyspace()) + .append(".") + .append(getSignature().getName()) + .append("("); + boolean first = true; + for (int i = 0; i < getSignature().getParameterTypes().size(); i++) { + if (first) { + first = false; + } else { + builder.append(","); + } + DataType type = getSignature().getParameterTypes().get(i); + CqlIdentifier name = getParameterNames().get(i); + builder.append(name).append(" ").append(type.asCql(false, pretty)); + } + builder + .append(")") + .increaseIndent() + .newLine() + .append(isCalledOnNullInput() ? "CALLED ON NULL INPUT" : "RETURNS NULL ON NULL INPUT") + .newLine() + .append("RETURNS ") + .append(getReturnType().asCql(false, true)) + .newLine(); + // handle deterministic and monotonic + if (getDeterministic().orElse(false)) { + builder.append("DETERMINISTIC").newLine(); + } + if (getMonotonicity().isPresent()) { + switch (getMonotonicity().get()) { + case FULLY_MONOTONIC: + builder.append("MONOTONIC").newLine(); + break; + case PARTIALLY_MONOTONIC: + builder.append("MONOTONIC ON ").append(getMonotonicArgumentNames().get(0)).newLine(); + break; + default: + break; + } + } + builder + .append("LANGUAGE ") + .append(getLanguage()) + .newLine() + .append("AS '") + .append(getBody()) + .append("';"); + return builder.build(); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/metadata/schema/DseGraphKeyspaceMetadata.java b/core/src/main/java/com/datastax/dse/driver/api/core/metadata/schema/DseGraphKeyspaceMetadata.java new file mode 100644 index 00000000000..8978a8858f9 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/metadata/schema/DseGraphKeyspaceMetadata.java @@ -0,0 +1,74 @@ +/* + * 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 com.datastax.dse.driver.api.core.metadata.schema; + +import com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata; +import com.datastax.oss.driver.internal.core.metadata.schema.ScriptBuilder; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.Map; +import java.util.Optional; + +/** + * Specialized keyspace metadata, that handles the graph-specific properties introduced in DSE 6.8. + * + *

This type only exists to avoid breaking binary compatibility. When the driver is connected to + * a DSE cluster, all the {@link KeyspaceMetadata} instances it returns can be safely downcast to + * this interface. + */ +public interface DseGraphKeyspaceMetadata extends DseKeyspaceMetadata { + + /** The graph engine that will be used to interpret this keyspace. */ + @NonNull + Optional getGraphEngine(); + + @NonNull + @Override + default String describe(boolean pretty) { + ScriptBuilder builder = new ScriptBuilder(pretty); + if (isVirtual()) { + builder.append("/* VIRTUAL "); + } else { + builder.append("CREATE "); + } + builder + .append("KEYSPACE ") + .append(getName()) + .append(" WITH replication = { 'class' : '") + .append(getReplication().get("class")) + .append("'"); + for (Map.Entry entry : getReplication().entrySet()) { + if (!entry.getKey().equals("class")) { + builder + .append(", '") + .append(entry.getKey()) + .append("': '") + .append(entry.getValue()) + .append("'"); + } + } + builder.append(" } AND durable_writes = ").append(Boolean.toString(isDurableWrites())); + getGraphEngine() + .ifPresent( + graphEngine -> builder.append(" AND graph_engine ='").append(graphEngine).append("'")); + builder.append(";"); + if (isVirtual()) { + builder.append(" */"); + } + return builder.build(); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/metadata/schema/DseGraphTableMetadata.java b/core/src/main/java/com/datastax/dse/driver/api/core/metadata/schema/DseGraphTableMetadata.java new file mode 100644 index 00000000000..8f340b3b447 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/metadata/schema/DseGraphTableMetadata.java @@ -0,0 +1,157 @@ +/* + * 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 com.datastax.dse.driver.api.core.metadata.schema; + +import com.datastax.dse.driver.internal.core.metadata.schema.ScriptHelper; +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.api.core.metadata.schema.ClusteringOrder; +import com.datastax.oss.driver.api.core.metadata.schema.ColumnMetadata; +import com.datastax.oss.driver.api.core.metadata.schema.TableMetadata; +import com.datastax.oss.driver.internal.core.metadata.schema.ScriptBuilder; +import com.datastax.oss.driver.internal.core.metadata.schema.parsing.RelationParser; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.Map; +import java.util.Optional; + +/** + * Specialized table metadata, that handles the graph-specific properties introduced in DSE 6.8. + * + *

This type only exists to avoid breaking binary compatibility. When the driver is connected to + * a DSE cluster, all the {@link TableMetadata} instances it returns can be safely downcast to this + * interface. + */ +public interface DseGraphTableMetadata extends DseTableMetadata { + /** + * The vertex metadata if this table represents a vertex in graph, otherwise empty. + * + *

This is mutually exclusive with {@link #getEdge()}. + */ + @NonNull + Optional getVertex(); + + /** + * The edge metadata if this table represents an edge in graph, otherwise empty. + * + *

This is mutually exclusive with {@link #getVertex()}. + */ + @NonNull + Optional getEdge(); + + @NonNull + @Override + default String describe(boolean pretty) { + ScriptBuilder builder = new ScriptBuilder(pretty); + if (isVirtual()) { + builder.append("/* VIRTUAL "); + } else { + builder.append("CREATE "); + } + + builder + .append("TABLE ") + .append(getKeyspace()) + .append(".") + .append(getName()) + .append(" (") + .newLine() + .increaseIndent(); + + for (ColumnMetadata column : getColumns().values()) { + builder.append(column.getName()).append(" ").append(column.getType().asCql(true, pretty)); + if (column.isStatic()) { + builder.append(" static"); + } + builder.append(",").newLine(); + } + + // PK + builder.append("PRIMARY KEY ("); + if (getPartitionKey().size() == 1) { // PRIMARY KEY (k + builder.append(getPartitionKey().get(0).getName()); + } else { // PRIMARY KEY ((k1, k2) + builder.append("("); + boolean first = true; + for (ColumnMetadata pkColumn : getPartitionKey()) { + if (first) { + first = false; + } else { + builder.append(", "); + } + builder.append(pkColumn.getName()); + } + builder.append(")"); + } + // PRIMARY KEY (, cc1, cc2, cc3) + for (ColumnMetadata clusteringColumn : getClusteringColumns().keySet()) { + builder.append(", ").append(clusteringColumn.getName()); + } + builder.append(")"); + + builder.newLine().decreaseIndent().append(")"); + + builder.increaseIndent(); + if (isCompactStorage()) { + builder.andWith().append("COMPACT STORAGE"); + } + if (getClusteringColumns().containsValue(ClusteringOrder.DESC)) { + builder.andWith().append("CLUSTERING ORDER BY ("); + boolean first = true; + for (Map.Entry entry : + getClusteringColumns().entrySet()) { + if (first) { + first = false; + } else { + builder.append(", "); + } + builder.append(entry.getKey().getName()).append(" ").append(entry.getValue().name()); + } + builder.append(")"); + } + getVertex() + .ifPresent( + vertex -> { + builder.andWith().append("VERTEX LABEL").append(" ").append(vertex.getLabelName()); + }); + getEdge() + .ifPresent( + edge -> { + builder.andWith().append("EDGE LABEL").append(" ").append(edge.getLabelName()); + ScriptHelper.appendEdgeSide( + builder, + edge.getFromTable(), + edge.getFromLabel(), + edge.getFromPartitionKeyColumns(), + edge.getFromClusteringColumns(), + "FROM"); + ScriptHelper.appendEdgeSide( + builder, + edge.getToTable(), + edge.getToLabel(), + edge.getToPartitionKeyColumns(), + edge.getToClusteringColumns(), + "TO"); + }); + Map options = getOptions(); + RelationParser.appendOptions(options, builder); + builder.append(";"); + if (isVirtual()) { + builder.append(" */"); + } + return builder.build(); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/metadata/schema/DseIndexMetadata.java b/core/src/main/java/com/datastax/dse/driver/api/core/metadata/schema/DseIndexMetadata.java new file mode 100644 index 00000000000..ac4c1057fbf --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/metadata/schema/DseIndexMetadata.java @@ -0,0 +1,28 @@ +/* + * 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 com.datastax.dse.driver.api.core.metadata.schema; + +import com.datastax.oss.driver.api.core.metadata.schema.IndexMetadata; + +/** + * Specialized index metadata for DSE. + * + *

This type exists only for future extensibility; currently, it is identical to {@link + * IndexMetadata}. + */ +public interface DseIndexMetadata extends IndexMetadata {} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/metadata/schema/DseKeyspaceMetadata.java b/core/src/main/java/com/datastax/dse/driver/api/core/metadata/schema/DseKeyspaceMetadata.java new file mode 100644 index 00000000000..bc5cb002802 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/metadata/schema/DseKeyspaceMetadata.java @@ -0,0 +1,35 @@ +/* + * 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 com.datastax.dse.driver.api.core.metadata.schema; + +import com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata; +import com.datastax.oss.driver.api.core.metadata.schema.TableMetadata; + +/** + * Specialized keyspace metadata for DSE. + * + *

Notes: + * + *

    + *
  • this type can always be safely downcast to {@link DseGraphKeyspaceMetadata} (the only + * reason the two interfaces are separate is for backward compatibility). + *
  • all returned elements can be cast to their DSE counterparts, for example {@link + * TableMetadata} to {@link DseTableMetadata}. + *
+ */ +public interface DseKeyspaceMetadata extends KeyspaceMetadata {} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/metadata/schema/DseRelationMetadata.java b/core/src/main/java/com/datastax/dse/driver/api/core/metadata/schema/DseRelationMetadata.java new file mode 100644 index 00000000000..55b36cb7fe5 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/metadata/schema/DseRelationMetadata.java @@ -0,0 +1,31 @@ +/* + * 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 com.datastax.dse.driver.api.core.metadata.schema; + +import com.datastax.oss.driver.api.core.metadata.schema.ColumnMetadata; +import com.datastax.oss.driver.api.core.metadata.schema.RelationMetadata; + +/** + * Specialized table or materialized view metadata for DSE. + * + *

This type exists only for future extensibility; currently, it is identical to {@link + * RelationMetadata}. + * + *

Note that all returned {@link ColumnMetadata} can be cast to {@link DseColumnMetadata}. + */ +public interface DseRelationMetadata extends RelationMetadata {} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/metadata/schema/DseTableMetadata.java b/core/src/main/java/com/datastax/dse/driver/api/core/metadata/schema/DseTableMetadata.java new file mode 100644 index 00000000000..a140f93bc2e --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/metadata/schema/DseTableMetadata.java @@ -0,0 +1,36 @@ +/* + * 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 com.datastax.dse.driver.api.core.metadata.schema; + +import com.datastax.oss.driver.api.core.metadata.schema.ColumnMetadata; +import com.datastax.oss.driver.api.core.metadata.schema.IndexMetadata; +import com.datastax.oss.driver.api.core.metadata.schema.TableMetadata; + +/** + * Specialized table metadata for DSE. + * + *

Notes: + * + *

    + *
  • this type can always be safely downcast to {@link DseGraphTableMetadata} (the only reason + * the two interfaces are separate is for backward compatibility). + *
  • all returned {@link ColumnMetadata} can be cast to {@link DseColumnMetadata}, and all + * {@link IndexMetadata} to {@link DseIndexMetadata}. + *
+ */ +public interface DseTableMetadata extends DseRelationMetadata, TableMetadata {} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/metadata/schema/DseVertexMetadata.java b/core/src/main/java/com/datastax/dse/driver/api/core/metadata/schema/DseVertexMetadata.java new file mode 100644 index 00000000000..c08a7eb1d60 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/metadata/schema/DseVertexMetadata.java @@ -0,0 +1,29 @@ +/* + * 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 com.datastax.dse.driver.api.core.metadata.schema; + +import com.datastax.oss.driver.api.core.CqlIdentifier; +import edu.umd.cs.findbugs.annotations.NonNull; + +/** Vertex metadata, for a table that was created with CREATE TABLE ... WITH VERTEX LABEL. */ +public interface DseVertexMetadata { + + /** The label of the vertex in graph. */ + @NonNull + CqlIdentifier getLabelName(); +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/metadata/schema/DseViewMetadata.java b/core/src/main/java/com/datastax/dse/driver/api/core/metadata/schema/DseViewMetadata.java new file mode 100644 index 00000000000..0f68ea7e456 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/metadata/schema/DseViewMetadata.java @@ -0,0 +1,31 @@ +/* + * 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 com.datastax.dse.driver.api.core.metadata.schema; + +import com.datastax.oss.driver.api.core.metadata.schema.ColumnMetadata; +import com.datastax.oss.driver.api.core.metadata.schema.ViewMetadata; + +/** + * Specialized materialized view metadata for DSE. + * + *

This type exists only for future extensibility; currently, it is identical to {@link + * ViewMetadata}. + * + *

Note that all returned {@link ColumnMetadata} can be cast to {@link DseColumnMetadata}. + */ +public interface DseViewMetadata extends DseRelationMetadata, ViewMetadata {} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/metrics/DseNodeMetric.java b/core/src/main/java/com/datastax/dse/driver/api/core/metrics/DseNodeMetric.java new file mode 100644 index 00000000000..cf4b4d0aa18 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/metrics/DseNodeMetric.java @@ -0,0 +1,59 @@ +/* + * 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 com.datastax.dse.driver.api.core.metrics; + +import com.datastax.oss.driver.api.core.metrics.NodeMetric; +import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableMap; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.Map; + +/** See {@code reference.conf} for a description of each metric. */ +public enum DseNodeMetric implements NodeMetric { + GRAPH_MESSAGES("graph-messages"); + + private static final Map BY_PATH = sortByPath(); + + private final String path; + + DseNodeMetric(String path) { + this.path = path; + } + + @Override + @NonNull + public String getPath() { + return path; + } + + @NonNull + public static DseNodeMetric fromPath(@NonNull String path) { + DseNodeMetric metric = BY_PATH.get(path); + if (metric == null) { + throw new IllegalArgumentException("Unknown node metric path " + path); + } + return metric; + } + + private static Map sortByPath() { + ImmutableMap.Builder result = ImmutableMap.builder(); + for (DseNodeMetric value : values()) { + result.put(value.getPath(), value); + } + return result.build(); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/metrics/DseSessionMetric.java b/core/src/main/java/com/datastax/dse/driver/api/core/metrics/DseSessionMetric.java new file mode 100644 index 00000000000..79584f3c44a --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/metrics/DseSessionMetric.java @@ -0,0 +1,62 @@ +/* + * 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 com.datastax.dse.driver.api.core.metrics; + +import com.datastax.oss.driver.api.core.metrics.SessionMetric; +import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableMap; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.Map; + +/** See {@code reference.conf} for a description of each metric. */ +public enum DseSessionMetric implements SessionMetric { + CONTINUOUS_CQL_REQUESTS("continuous-cql-requests"), + GRAPH_REQUESTS("graph-requests"), + GRAPH_CLIENT_TIMEOUTS("graph-client-timeouts"), + ; + + private static final Map BY_PATH = sortByPath(); + + private final String path; + + DseSessionMetric(String path) { + this.path = path; + } + + @NonNull + @Override + public String getPath() { + return path; + } + + @NonNull + public static DseSessionMetric fromPath(@NonNull String path) { + DseSessionMetric metric = BY_PATH.get(path); + if (metric == null) { + throw new IllegalArgumentException("Unknown DSE session metric path " + path); + } + return metric; + } + + private static Map sortByPath() { + ImmutableMap.Builder result = ImmutableMap.builder(); + for (DseSessionMetric value : values()) { + result.put(value.getPath(), value); + } + return result.build(); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/servererrors/UnfitClientException.java b/core/src/main/java/com/datastax/dse/driver/api/core/servererrors/UnfitClientException.java new file mode 100644 index 00000000000..8bf4d80699d --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/servererrors/UnfitClientException.java @@ -0,0 +1,55 @@ +/* + * 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 com.datastax.dse.driver.api.core.servererrors; + +import com.datastax.oss.driver.api.core.cql.ExecutionInfo; +import com.datastax.oss.driver.api.core.metadata.Node; +import com.datastax.oss.driver.api.core.servererrors.CoordinatorException; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; + +/** + * A server-side error triggered when DSE can't send asynchronous results back to the client. + * + *

Currently, this is used when the client is unable to keep up with the rate during a continuous + * paging session. + * + *

Note that the protocol specification refers to this error as {@code CLIENT_WRITE_FAILURE}; we + * don't follow that terminology because it would be too misleading (this is not a client error, and + * it doesn't occur while writing data to DSE). + */ +public class UnfitClientException extends CoordinatorException { + + public UnfitClientException(@NonNull Node coordinator, @NonNull String message) { + this(coordinator, message, null, false); + } + + private UnfitClientException( + @NonNull Node coordinator, + @NonNull String message, + @Nullable ExecutionInfo executionInfo, + boolean writableStackTrace) { + super(coordinator, message, executionInfo, writableStackTrace); + } + + @Override + @NonNull + public UnfitClientException copy() { + return new UnfitClientException(getCoordinator(), getMessage(), getExecutionInfo(), true); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/type/DseDataTypes.java b/core/src/main/java/com/datastax/dse/driver/api/core/type/DseDataTypes.java new file mode 100644 index 00000000000..6003274e09a --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/type/DseDataTypes.java @@ -0,0 +1,37 @@ +/* + * 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 com.datastax.dse.driver.api.core.type; + +import com.datastax.oss.driver.api.core.type.CustomType; +import com.datastax.oss.driver.api.core.type.DataTypes; + +/** Extends {@link DataTypes} to handle DSE-specific types. */ +public class DseDataTypes extends DataTypes { + + public static final CustomType LINE_STRING = + (CustomType) custom("org.apache.cassandra.db.marshal.LineStringType"); + + public static final CustomType POINT = + (CustomType) custom("org.apache.cassandra.db.marshal.PointType"); + + public static final CustomType POLYGON = + (CustomType) custom("org.apache.cassandra.db.marshal.PolygonType"); + + public static final CustomType DATE_RANGE = + (CustomType) custom("org.apache.cassandra.db.marshal.DateRangeType"); +} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/type/codec/DseTypeCodecs.java b/core/src/main/java/com/datastax/dse/driver/api/core/type/codec/DseTypeCodecs.java new file mode 100644 index 00000000000..fb0225970b4 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/api/core/type/codec/DseTypeCodecs.java @@ -0,0 +1,41 @@ +/* + * 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 com.datastax.dse.driver.api.core.type.codec; + +import com.datastax.dse.driver.api.core.data.geometry.LineString; +import com.datastax.dse.driver.api.core.data.geometry.Point; +import com.datastax.dse.driver.api.core.data.geometry.Polygon; +import com.datastax.dse.driver.api.core.data.time.DateRange; +import com.datastax.dse.driver.internal.core.type.codec.geometry.LineStringCodec; +import com.datastax.dse.driver.internal.core.type.codec.geometry.PointCodec; +import com.datastax.dse.driver.internal.core.type.codec.geometry.PolygonCodec; +import com.datastax.dse.driver.internal.core.type.codec.time.DateRangeCodec; +import com.datastax.oss.driver.api.core.type.codec.TypeCodec; +import com.datastax.oss.driver.api.core.type.codec.TypeCodecs; + +/** Extends {@link TypeCodecs} to handle DSE-specific types. */ +public class DseTypeCodecs extends TypeCodecs { + + public static final TypeCodec LINE_STRING = new LineStringCodec(); + + public static final TypeCodec POINT = new PointCodec(); + + public static final TypeCodec POLYGON = new PolygonCodec(); + + public static final TypeCodec DATE_RANGE = new DateRangeCodec(); +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/DseProtocolFeature.java b/core/src/main/java/com/datastax/dse/driver/internal/core/DseProtocolFeature.java new file mode 100644 index 00000000000..95f245061d2 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/DseProtocolFeature.java @@ -0,0 +1,38 @@ +/* + * 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 com.datastax.dse.driver.internal.core; + +import com.datastax.oss.driver.internal.core.ProtocolFeature; + +/** + * Features that are supported by DataStax Enterprise (DSE) protocol versions. + * + * @see com.datastax.dse.driver.api.core.DseProtocolVersion + * @see com.datastax.oss.driver.internal.core.DefaultProtocolFeature + */ +public enum DseProtocolFeature implements ProtocolFeature { + + /** + * The ability to execute continuous paging requests. + * + * @see CASSANDRA-11521 + * @see com.datastax.dse.driver.api.core.cql.continuous.ContinuousSession + */ + CONTINUOUS_PAGING, + ; +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/InsightsClientLifecycleListener.java b/core/src/main/java/com/datastax/dse/driver/internal/core/InsightsClientLifecycleListener.java new file mode 100644 index 00000000000..e4dd6f93bf7 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/InsightsClientLifecycleListener.java @@ -0,0 +1,66 @@ +/* + * 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 com.datastax.dse.driver.internal.core; + +import static com.datastax.dse.driver.api.core.config.DseDriverOption.MONITOR_REPORTING_ENABLED; + +import com.datastax.dse.driver.internal.core.insights.InsightsClient; +import com.datastax.dse.driver.internal.core.insights.configuration.InsightsConfiguration; +import com.datastax.oss.driver.internal.core.context.InternalDriverContext; +import com.datastax.oss.driver.internal.core.context.LifecycleListener; + +public class InsightsClientLifecycleListener implements LifecycleListener { + private static final boolean DEFAULT_INSIGHTS_ENABLED = true; + private static final long STATUS_EVENT_DELAY_MILLIS = 300000L; + private final InternalDriverContext context; + private final StackTraceElement[] initCallStackTrace; + private volatile InsightsClient insightsClient; + + public InsightsClientLifecycleListener( + InternalDriverContext context, StackTraceElement[] initCallStackTrace) { + this.context = context; + this.initCallStackTrace = initCallStackTrace; + } + + @Override + public void onSessionReady() { + boolean monitorReportingEnabled = + context + .getConfig() + .getDefaultProfile() + .getBoolean(MONITOR_REPORTING_ENABLED, DEFAULT_INSIGHTS_ENABLED); + + this.insightsClient = + InsightsClient.createInsightsClient( + new InsightsConfiguration( + monitorReportingEnabled, + STATUS_EVENT_DELAY_MILLIS, + context.getNettyOptions().adminEventExecutorGroup().next()), + context, + initCallStackTrace); + insightsClient.sendStartupMessage(); + insightsClient.scheduleStatusMessageSend(); + } + + @Override + public void close() { + if (insightsClient != null) { + insightsClient.shutdown(); + } + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/auth/AuthUtils.java b/core/src/main/java/com/datastax/dse/driver/internal/core/auth/AuthUtils.java new file mode 100644 index 00000000000..38f1644bcb7 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/auth/AuthUtils.java @@ -0,0 +1,58 @@ +/* + * 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 com.datastax.dse.driver.internal.core.auth; + +import com.datastax.oss.driver.api.core.auth.AuthenticationException; +import com.datastax.oss.driver.api.core.config.DriverExecutionProfile; +import com.datastax.oss.driver.api.core.config.DriverOption; +import com.datastax.oss.driver.api.core.metadata.EndPoint; +import java.util.ArrayList; +import java.util.List; + +public class AuthUtils { + /** + * Utility function that checks for the existence of settings and throws an exception if they + * aren't present + * + * @param config Current working driver configuration + * @param authenticatorName name of authenticator for logging purposes + * @param endPoint the host we are attempting to authenticate to + * @param options a list of DriverOptions to check to see if they are present + */ + public static void validateConfigPresent( + DriverExecutionProfile config, + String authenticatorName, + EndPoint endPoint, + DriverOption... options) { + List missingOptions = new ArrayList<>(); + for (DriverOption option : options) { + + if (!config.isDefined(option)) { + missingOptions.add(option); + } + if (missingOptions.size() > 0) { + String message = + "Missing required configuration options for authenticator " + authenticatorName + ":"; + for (DriverOption missingOption : missingOptions) { + message = message + " " + missingOption.getPath(); + } + throw new AuthenticationException(endPoint, message); + } + } + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/auth/DseGssApiAuthProvider.java b/core/src/main/java/com/datastax/dse/driver/internal/core/auth/DseGssApiAuthProvider.java new file mode 100644 index 00000000000..6ef6596a870 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/auth/DseGssApiAuthProvider.java @@ -0,0 +1,198 @@ +/* + * 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 com.datastax.dse.driver.internal.core.auth; + +import com.datastax.dse.driver.api.core.auth.DseGssApiAuthProviderBase; +import com.datastax.dse.driver.api.core.config.DseDriverOption; +import com.datastax.oss.driver.api.core.auth.AuthProvider; +import com.datastax.oss.driver.api.core.config.DriverExecutionProfile; +import com.datastax.oss.driver.api.core.context.DriverContext; +import com.datastax.oss.driver.api.core.metadata.EndPoint; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.Map; +import net.jcip.annotations.ThreadSafe; + +/** + * {@link AuthProvider} that provides GSSAPI authenticator instances for clients to connect to DSE + * clusters secured with {@code DseAuthenticator}. + * + *

To activate this provider an {@code auth-provider} section must be included in the driver + * configuration, for example: + * + *

+ * dse-java-driver {
+ *  auth-provider {
+ *      class = com.datastax.dse.driver.internal.core.auth.DseGssApiAuthProvider
+ *      login-configuration {
+ *          principal = "user principal here ex cassandra@DATASTAX.COM"
+ *          useKeyTab = "true"
+ *          refreshKrb5Config = "true"
+ *          keyTab = "Path to keytab file here"
+ *      }
+ *   }
+ * }
+ * 
+ * + *

Kerberos Authentication

+ * + * Keytab and ticket cache settings are specified using a standard JAAS configuration file. The + * location of the file can be set using the java.security.auth.login.config system + * property or by adding a login.config.url.n entry in the java.security + * properties file. Alternatively a login-configuration section can be included in the driver + * configuration. + * + *

See the following documents for further details: + * + *

    + *
  1. JAAS + * Login Configuration File; + *
  2. Krb5LoginModule + * options; + *
  3. JAAS + * Authentication Tutorial for more on JAAS in general. + *
+ * + *

Authentication using ticket cache

+ * + * Run kinit to obtain a ticket and populate the cache before connecting. JAAS config: + * + *
+ * DseClient {
+ *   com.sun.security.auth.module.Krb5LoginModule required
+ *     useTicketCache=true
+ *     renewTGT=true;
+ * };
+ * 
+ * + *

Authentication using a keytab file

+ * + * To enable authentication using a keytab file, specify its location on disk. If your keytab + * contains more than one principal key, you should also specify which one to select. This + * information can also be specified in the driver config, under the login-configuration section. + * + *
+ * DseClient {
+ *     com.sun.security.auth.module.Krb5LoginModule required
+ *       useKeyTab=true
+ *       keyTab="/path/to/file.keytab"
+ *       principal="user@MYDOMAIN.COM";
+ * };
+ * 
+ * + *

Specifying SASL protocol name

+ * + * The SASL protocol name used by this auth provider defaults to " + * {@value #DEFAULT_SASL_SERVICE_NAME}". + * + *

Important: the SASL protocol name should match the username of the Kerberos + * service principal used by the DSE server. This information is specified in the dse.yaml file by + * the {@code service_principal} option under the kerberos_options + * section, and may vary from one DSE installation to another – especially if you installed + * DSE with an automated package installer. + * + *

For example, if your dse.yaml file contains the following: + * + *

{@code
+ * kerberos_options:
+ *     ...
+ *     service_principal: cassandra/my.host.com@MY.REALM.COM
+ * }
+ * + * The correct SASL protocol name to use when authenticating against this DSE server is "{@code + * cassandra}". + * + *

Should you need to change the SASL protocol name, use one of the methods below: + * + *

    + *
  1. Specify the service name in the driver config. + *
    + * dse-java-driver {
    + *   auth-provider {
    + *     class = com.datastax.dse.driver.internal.core.auth.DseGssApiAuthProvider
    + *     service = "alternate"
    + *   }
    + * }
    + * 
    + *
  2. Specify the service name with the {@code dse.sasl.service} system property when starting + * your application, e.g. {@code -Ddse.sasl.service=cassandra}. + *
+ * + * If a non-null SASL service name is provided to the aforementioned config, that name takes + * precedence over the contents of the {@code dse.sasl.service} system property. + * + *

Should internal sasl properties need to be set such as qop. This can be accomplished by + * including a sasl-properties in the driver config, for example: + * + *

+ * dse-java-driver {
+ *   auth-provider {
+ *     class = com.datastax.dse.driver.internal.core.auth.DseGssApiAuthProvider
+ *     sasl-properties {
+ *       javax.security.sasl.qop = "auth-conf"
+ *     }
+ *   }
+ * }
+ * 
+ */ +@ThreadSafe +public class DseGssApiAuthProvider extends DseGssApiAuthProviderBase { + + private final DriverExecutionProfile config; + + public DseGssApiAuthProvider(DriverContext context) { + super(context.getSessionName()); + + this.config = context.getConfig().getDefaultProfile(); + } + + @NonNull + @Override + protected GssApiOptions getOptions( + @NonNull EndPoint endPoint, @NonNull String serverAuthenticator) { + // A login configuration is always necessary, throw an exception if that option is missing. + AuthUtils.validateConfigPresent( + config, + DseGssApiAuthProvider.class.getName(), + endPoint, + DseDriverOption.AUTH_PROVIDER_LOGIN_CONFIGURATION); + + GssApiOptions.Builder optionsBuilder = GssApiOptions.builder(); + + if (config.isDefined(DseDriverOption.AUTH_PROVIDER_AUTHORIZATION_ID)) { + optionsBuilder.withAuthorizationId( + config.getString(DseDriverOption.AUTH_PROVIDER_AUTHORIZATION_ID)); + } + if (config.isDefined(DseDriverOption.AUTH_PROVIDER_SERVICE)) { + optionsBuilder.withSaslProtocol(config.getString(DseDriverOption.AUTH_PROVIDER_SERVICE)); + } + if (config.isDefined(DseDriverOption.AUTH_PROVIDER_SASL_PROPERTIES)) { + for (Map.Entry entry : + config.getStringMap(DseDriverOption.AUTH_PROVIDER_SASL_PROPERTIES).entrySet()) { + optionsBuilder.addSaslProperty(entry.getKey(), entry.getValue()); + } + } + Map loginConfigurationMap = + config.getStringMap(DseDriverOption.AUTH_PROVIDER_LOGIN_CONFIGURATION); + optionsBuilder.withLoginConfiguration(loginConfigurationMap); + return optionsBuilder.build(); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/auth/DsePlainTextAuthProvider.java b/core/src/main/java/com/datastax/dse/driver/internal/core/auth/DsePlainTextAuthProvider.java new file mode 100644 index 00000000000..6cf82aef03e --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/auth/DsePlainTextAuthProvider.java @@ -0,0 +1,36 @@ +/* + * 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 com.datastax.dse.driver.internal.core.auth; + +import com.datastax.oss.driver.api.core.context.DriverContext; +import com.datastax.oss.driver.internal.core.auth.PlainTextAuthProvider; +import net.jcip.annotations.ThreadSafe; + +/** + * @deprecated The driver's default plain text providers now support both Apache Cassandra and DSE. + * This type was preserved for backward compatibility, but {@link PlainTextAuthProvider} should + * be used instead. + */ +@ThreadSafe +@Deprecated +public class DsePlainTextAuthProvider extends PlainTextAuthProvider { + + public DsePlainTextAuthProvider(DriverContext context) { + super(context); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/cql/DseConversions.java b/core/src/main/java/com/datastax/dse/driver/internal/core/cql/DseConversions.java new file mode 100644 index 00000000000..15aab143150 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/cql/DseConversions.java @@ -0,0 +1,155 @@ +/* + * 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 com.datastax.dse.driver.internal.core.cql; + +import com.datastax.dse.driver.api.core.config.DseDriverOption; +import com.datastax.dse.driver.api.core.servererrors.UnfitClientException; +import com.datastax.dse.protocol.internal.DseProtocolConstants; +import com.datastax.dse.protocol.internal.request.query.ContinuousPagingOptions; +import com.datastax.dse.protocol.internal.request.query.DseQueryOptions; +import com.datastax.oss.driver.api.core.ConsistencyLevel; +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.api.core.ProtocolVersion; +import com.datastax.oss.driver.api.core.config.DefaultDriverOption; +import com.datastax.oss.driver.api.core.config.DriverExecutionProfile; +import com.datastax.oss.driver.api.core.cql.BoundStatement; +import com.datastax.oss.driver.api.core.cql.PreparedStatement; +import com.datastax.oss.driver.api.core.cql.SimpleStatement; +import com.datastax.oss.driver.api.core.cql.Statement; +import com.datastax.oss.driver.api.core.metadata.Node; +import com.datastax.oss.driver.api.core.servererrors.CoordinatorException; +import com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry; +import com.datastax.oss.driver.internal.core.ConsistencyLevelRegistry; +import com.datastax.oss.driver.internal.core.DefaultProtocolFeature; +import com.datastax.oss.driver.internal.core.ProtocolVersionRegistry; +import com.datastax.oss.driver.internal.core.context.InternalDriverContext; +import com.datastax.oss.driver.internal.core.cql.Conversions; +import com.datastax.oss.protocol.internal.Message; +import com.datastax.oss.protocol.internal.request.Execute; +import com.datastax.oss.protocol.internal.request.Query; +import com.datastax.oss.protocol.internal.response.Error; +import com.datastax.oss.protocol.internal.util.Bytes; +import java.nio.ByteBuffer; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class DseConversions { + + public static Message toContinuousPagingMessage( + Statement statement, DriverExecutionProfile config, InternalDriverContext context) { + ConsistencyLevelRegistry consistencyLevelRegistry = context.getConsistencyLevelRegistry(); + ConsistencyLevel consistency = statement.getConsistencyLevel(); + int consistencyCode = + (consistency == null) + ? consistencyLevelRegistry.nameToCode( + config.getString(DefaultDriverOption.REQUEST_CONSISTENCY)) + : consistency.getProtocolCode(); + int pageSize = config.getInt(DseDriverOption.CONTINUOUS_PAGING_PAGE_SIZE); + boolean pageSizeInBytes = config.getBoolean(DseDriverOption.CONTINUOUS_PAGING_PAGE_SIZE_BYTES); + int maxPages = config.getInt(DseDriverOption.CONTINUOUS_PAGING_MAX_PAGES); + int maxPagesPerSecond = config.getInt(DseDriverOption.CONTINUOUS_PAGING_MAX_PAGES_PER_SECOND); + int maxEnqueuedPages = config.getInt(DseDriverOption.CONTINUOUS_PAGING_MAX_ENQUEUED_PAGES); + ContinuousPagingOptions options = + new ContinuousPagingOptions(maxPages, maxPagesPerSecond, maxEnqueuedPages); + ConsistencyLevel serialConsistency = statement.getSerialConsistencyLevel(); + int serialConsistencyCode = + (serialConsistency == null) + ? consistencyLevelRegistry.nameToCode( + config.getString(DefaultDriverOption.REQUEST_SERIAL_CONSISTENCY)) + : serialConsistency.getProtocolCode(); + long timestamp = statement.getQueryTimestamp(); + if (timestamp == Statement.NO_DEFAULT_TIMESTAMP) { + timestamp = context.getTimestampGenerator().next(); + } + CodecRegistry codecRegistry = context.getCodecRegistry(); + ProtocolVersion protocolVersion = context.getProtocolVersion(); + ProtocolVersionRegistry protocolVersionRegistry = context.getProtocolVersionRegistry(); + CqlIdentifier keyspace = statement.getKeyspace(); + if (statement instanceof SimpleStatement) { + SimpleStatement simpleStatement = (SimpleStatement) statement; + List positionalValues = simpleStatement.getPositionalValues(); + Map namedValues = simpleStatement.getNamedValues(); + if (!positionalValues.isEmpty() && !namedValues.isEmpty()) { + throw new IllegalArgumentException( + "Can't have both positional and named values in a statement."); + } + if (keyspace != null + && !protocolVersionRegistry.supports( + protocolVersion, DefaultProtocolFeature.PER_REQUEST_KEYSPACE)) { + throw new IllegalArgumentException( + "Can't use per-request keyspace with protocol " + protocolVersion); + } + DseQueryOptions queryOptions = + new DseQueryOptions( + consistencyCode, + Conversions.encode(positionalValues, codecRegistry, protocolVersion), + Conversions.encode(namedValues, codecRegistry, protocolVersion), + false, + pageSize, + statement.getPagingState(), + serialConsistencyCode, + timestamp, + (keyspace == null) ? null : keyspace.asInternal(), + pageSizeInBytes, + options); + return new Query(simpleStatement.getQuery(), queryOptions); + } else if (statement instanceof BoundStatement) { + BoundStatement boundStatement = (BoundStatement) statement; + if (!protocolVersionRegistry.supports( + protocolVersion, DefaultProtocolFeature.UNSET_BOUND_VALUES)) { + Conversions.ensureAllSet(boundStatement); + } + boolean skipMetadata = + boundStatement.getPreparedStatement().getResultSetDefinitions().size() > 0; + DseQueryOptions queryOptions = + new DseQueryOptions( + consistencyCode, + boundStatement.getValues(), + Collections.emptyMap(), + skipMetadata, + pageSize, + statement.getPagingState(), + serialConsistencyCode, + timestamp, + null, + pageSizeInBytes, + options); + PreparedStatement preparedStatement = boundStatement.getPreparedStatement(); + ByteBuffer id = preparedStatement.getId(); + ByteBuffer resultMetadataId = preparedStatement.getResultMetadataId(); + return new Execute( + Bytes.getArray(id), + (resultMetadataId == null) ? null : Bytes.getArray(resultMetadataId), + queryOptions); + } else { + throw new IllegalArgumentException( + "Unsupported statement type: " + statement.getClass().getName()); + } + } + + public static CoordinatorException toThrowable( + Node node, Error errorMessage, InternalDriverContext context) { + switch (errorMessage.code) { + case DseProtocolConstants.ErrorCode.CLIENT_WRITE_FAILURE: + return new UnfitClientException(node, errorMessage.message); + default: + return Conversions.toThrowable(node, errorMessage, context); + } + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/cql/continuous/ContinuousCqlRequestAsyncProcessor.java b/core/src/main/java/com/datastax/dse/driver/internal/core/cql/continuous/ContinuousCqlRequestAsyncProcessor.java new file mode 100644 index 00000000000..8a098bf2895 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/cql/continuous/ContinuousCqlRequestAsyncProcessor.java @@ -0,0 +1,56 @@ +/* + * 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 com.datastax.dse.driver.internal.core.cql.continuous; + +import com.datastax.dse.driver.api.core.cql.continuous.ContinuousAsyncResultSet; +import com.datastax.oss.driver.api.core.cql.Statement; +import com.datastax.oss.driver.api.core.session.Request; +import com.datastax.oss.driver.api.core.type.reflect.GenericType; +import com.datastax.oss.driver.internal.core.context.InternalDriverContext; +import com.datastax.oss.driver.internal.core.session.DefaultSession; +import com.datastax.oss.driver.internal.core.session.RequestProcessor; +import com.datastax.oss.driver.internal.core.util.concurrent.CompletableFutures; +import java.util.concurrent.CompletionStage; +import net.jcip.annotations.ThreadSafe; + +@ThreadSafe +public class ContinuousCqlRequestAsyncProcessor + implements RequestProcessor, CompletionStage> { + + public static final GenericType> + CONTINUOUS_RESULT_ASYNC = new GenericType>() {}; + + @Override + public boolean canProcess(Request request, GenericType resultType) { + return request instanceof Statement && resultType.equals(CONTINUOUS_RESULT_ASYNC); + } + + @Override + public CompletionStage process( + Statement request, + DefaultSession session, + InternalDriverContext context, + String sessionLogPrefix) { + return new ContinuousCqlRequestHandler(request, session, context, sessionLogPrefix).handle(); + } + + @Override + public CompletionStage newFailure(RuntimeException error) { + return CompletableFutures.failedFuture(error); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/cql/continuous/ContinuousCqlRequestHandler.java b/core/src/main/java/com/datastax/dse/driver/internal/core/cql/continuous/ContinuousCqlRequestHandler.java new file mode 100644 index 00000000000..dd308c11854 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/cql/continuous/ContinuousCqlRequestHandler.java @@ -0,0 +1,171 @@ +/* + * 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 com.datastax.dse.driver.internal.core.cql.continuous; + +import com.datastax.dse.driver.api.core.config.DseDriverOption; +import com.datastax.dse.driver.api.core.cql.continuous.ContinuousAsyncResultSet; +import com.datastax.dse.driver.api.core.metrics.DseSessionMetric; +import com.datastax.dse.driver.internal.core.cql.DseConversions; +import com.datastax.dse.protocol.internal.response.result.DseRowsMetadata; +import com.datastax.oss.driver.api.core.config.DriverExecutionProfile; +import com.datastax.oss.driver.api.core.cql.ColumnDefinitions; +import com.datastax.oss.driver.api.core.cql.ExecutionInfo; +import com.datastax.oss.driver.api.core.cql.Row; +import com.datastax.oss.driver.api.core.cql.Statement; +import com.datastax.oss.driver.api.core.metrics.DefaultNodeMetric; +import com.datastax.oss.driver.api.core.metrics.DefaultSessionMetric; +import com.datastax.oss.driver.internal.core.context.InternalDriverContext; +import com.datastax.oss.driver.internal.core.cql.Conversions; +import com.datastax.oss.driver.internal.core.cql.DefaultRow; +import com.datastax.oss.driver.internal.core.session.DefaultSession; +import com.datastax.oss.driver.internal.core.util.CountingIterator; +import com.datastax.oss.protocol.internal.Message; +import com.datastax.oss.protocol.internal.response.result.Rows; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.nio.ByteBuffer; +import java.time.Duration; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import net.jcip.annotations.ThreadSafe; + +/** + * Handles a request that supports multiple response messages (a.k.a. continuous paging request). + */ +@ThreadSafe +public class ContinuousCqlRequestHandler + extends ContinuousRequestHandlerBase, ContinuousAsyncResultSet> { + + ContinuousCqlRequestHandler( + @NonNull Statement statement, + @NonNull DefaultSession session, + @NonNull InternalDriverContext context, + @NonNull String sessionLogPrefix) { + super( + statement, + session, + context, + sessionLogPrefix, + ContinuousAsyncResultSet.class, + false, + DefaultSessionMetric.CQL_CLIENT_TIMEOUTS, + DseSessionMetric.CONTINUOUS_CQL_REQUESTS, + DefaultNodeMetric.CQL_MESSAGES); + // NOTE that ordering of the following statement matters. + // We should register this request after all fields have been initialized. + throttler.register(this); + } + + @NonNull + @Override + protected Duration getGlobalTimeout() { + return Duration.ZERO; + } + + @NonNull + @Override + protected Duration getPageTimeout(@NonNull Statement statement, int pageNumber) { + DriverExecutionProfile executionProfile = + Conversions.resolveExecutionProfile(statement, context); + if (pageNumber == 1) { + return executionProfile.getDuration(DseDriverOption.CONTINUOUS_PAGING_TIMEOUT_FIRST_PAGE); + } else { + return executionProfile.getDuration(DseDriverOption.CONTINUOUS_PAGING_TIMEOUT_OTHER_PAGES); + } + } + + @NonNull + @Override + protected Duration getReviseRequestTimeout(@NonNull Statement statement) { + DriverExecutionProfile executionProfile = + Conversions.resolveExecutionProfile(statement, context); + return executionProfile.getDuration(DseDriverOption.CONTINUOUS_PAGING_TIMEOUT_OTHER_PAGES); + } + + @Override + protected int getMaxEnqueuedPages(@NonNull Statement statement) { + DriverExecutionProfile executionProfile = + Conversions.resolveExecutionProfile(statement, context); + return executionProfile.getInt(DseDriverOption.CONTINUOUS_PAGING_MAX_ENQUEUED_PAGES); + } + + @Override + protected int getMaxPages(@NonNull Statement statement) { + DriverExecutionProfile executionProfile = + Conversions.resolveExecutionProfile(statement, context); + return executionProfile.getInt(DseDriverOption.CONTINUOUS_PAGING_MAX_PAGES); + } + + @NonNull + @Override + protected Message getMessage(@NonNull Statement statement) { + DriverExecutionProfile executionProfile = + Conversions.resolveExecutionProfile(statement, context); + return DseConversions.toContinuousPagingMessage(statement, executionProfile, context); + } + + @Override + protected boolean isTracingEnabled(@NonNull Statement statement) { + return false; + } + + @NonNull + @Override + protected Map createPayload(@NonNull Statement statement) { + return statement.getCustomPayload(); + } + + @NonNull + @Override + protected ContinuousAsyncResultSet createEmptyResultSet(@NonNull ExecutionInfo executionInfo) { + return DefaultContinuousAsyncResultSet.empty(executionInfo); + } + + @NonNull + @Override + protected DefaultContinuousAsyncResultSet createResultSet( + @NonNull Statement statement, + @NonNull Rows rows, + @NonNull ExecutionInfo executionInfo, + @NonNull ColumnDefinitions columnDefinitions) { + Queue> data = rows.getData(); + CountingIterator iterator = + new CountingIterator(data.size()) { + @Override + protected Row computeNext() { + List rowData = data.poll(); + return (rowData == null) + ? endOfData() + : new DefaultRow(columnDefinitions, rowData, context); + } + }; + DseRowsMetadata metadata = (DseRowsMetadata) rows.getMetadata(); + return new DefaultContinuousAsyncResultSet( + iterator, + columnDefinitions, + metadata.continuousPageNumber, + !metadata.isLastContinuousPage, + executionInfo, + this); + } + + @Override + protected int pageNumber(@NonNull ContinuousAsyncResultSet resultSet) { + return resultSet.pageNumber(); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/cql/continuous/ContinuousCqlRequestSyncProcessor.java b/core/src/main/java/com/datastax/dse/driver/internal/core/cql/continuous/ContinuousCqlRequestSyncProcessor.java new file mode 100644 index 00000000000..f151eb7eae2 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/cql/continuous/ContinuousCqlRequestSyncProcessor.java @@ -0,0 +1,67 @@ +/* + * 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 com.datastax.dse.driver.internal.core.cql.continuous; + +import com.datastax.dse.driver.api.core.cql.continuous.ContinuousAsyncResultSet; +import com.datastax.dse.driver.api.core.cql.continuous.ContinuousResultSet; +import com.datastax.oss.driver.api.core.cql.Statement; +import com.datastax.oss.driver.api.core.session.Request; +import com.datastax.oss.driver.api.core.type.reflect.GenericType; +import com.datastax.oss.driver.internal.core.context.InternalDriverContext; +import com.datastax.oss.driver.internal.core.session.DefaultSession; +import com.datastax.oss.driver.internal.core.session.RequestProcessor; +import com.datastax.oss.driver.internal.core.util.concurrent.BlockingOperation; +import com.datastax.oss.driver.internal.core.util.concurrent.CompletableFutures; +import net.jcip.annotations.ThreadSafe; + +@ThreadSafe +public class ContinuousCqlRequestSyncProcessor + implements RequestProcessor, ContinuousResultSet> { + + public static final GenericType CONTINUOUS_RESULT_SYNC = + GenericType.of(ContinuousResultSet.class); + + private final ContinuousCqlRequestAsyncProcessor asyncProcessor; + + public ContinuousCqlRequestSyncProcessor(ContinuousCqlRequestAsyncProcessor asyncProcessor) { + this.asyncProcessor = asyncProcessor; + } + + @Override + public boolean canProcess(Request request, GenericType resultType) { + return request instanceof Statement && resultType.equals(CONTINUOUS_RESULT_SYNC); + } + + @Override + public ContinuousResultSet process( + Statement request, + DefaultSession session, + InternalDriverContext context, + String sessionLogPrefix) { + BlockingOperation.checkNotDriverThread(); + ContinuousAsyncResultSet firstPage = + CompletableFutures.getUninterruptibly( + asyncProcessor.process(request, session, context, sessionLogPrefix)); + return new DefaultContinuousResultSet(firstPage); + } + + @Override + public ContinuousResultSet newFailure(RuntimeException error) { + throw error; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/cql/continuous/ContinuousRequestHandlerBase.java b/core/src/main/java/com/datastax/dse/driver/internal/core/cql/continuous/ContinuousRequestHandlerBase.java new file mode 100644 index 00000000000..0453022cb6a --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/cql/continuous/ContinuousRequestHandlerBase.java @@ -0,0 +1,1645 @@ +/* + * 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 com.datastax.dse.driver.internal.core.cql.continuous; + +import com.datastax.dse.driver.api.core.DseProtocolVersion; +import com.datastax.dse.driver.api.core.cql.continuous.ContinuousAsyncResultSet; +import com.datastax.dse.driver.internal.core.DseProtocolFeature; +import com.datastax.dse.driver.internal.core.cql.DseConversions; +import com.datastax.dse.protocol.internal.request.Revise; +import com.datastax.dse.protocol.internal.response.result.DseRowsMetadata; +import com.datastax.oss.driver.api.core.AllNodesFailedException; +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.api.core.DriverTimeoutException; +import com.datastax.oss.driver.api.core.NodeUnavailableException; +import com.datastax.oss.driver.api.core.ProtocolVersion; +import com.datastax.oss.driver.api.core.RequestThrottlingException; +import com.datastax.oss.driver.api.core.config.DefaultDriverOption; +import com.datastax.oss.driver.api.core.config.DriverExecutionProfile; +import com.datastax.oss.driver.api.core.connection.FrameTooLongException; +import com.datastax.oss.driver.api.core.cql.ColumnDefinitions; +import com.datastax.oss.driver.api.core.cql.ExecutionInfo; +import com.datastax.oss.driver.api.core.metadata.Node; +import com.datastax.oss.driver.api.core.metrics.DefaultNodeMetric; +import com.datastax.oss.driver.api.core.metrics.DefaultSessionMetric; +import com.datastax.oss.driver.api.core.metrics.NodeMetric; +import com.datastax.oss.driver.api.core.metrics.SessionMetric; +import com.datastax.oss.driver.api.core.retry.RetryPolicy; +import com.datastax.oss.driver.api.core.retry.RetryVerdict; +import com.datastax.oss.driver.api.core.servererrors.BootstrappingException; +import com.datastax.oss.driver.api.core.servererrors.CoordinatorException; +import com.datastax.oss.driver.api.core.servererrors.FunctionFailureException; +import com.datastax.oss.driver.api.core.servererrors.ProtocolError; +import com.datastax.oss.driver.api.core.servererrors.QueryValidationException; +import com.datastax.oss.driver.api.core.servererrors.ReadTimeoutException; +import com.datastax.oss.driver.api.core.servererrors.UnavailableException; +import com.datastax.oss.driver.api.core.servererrors.WriteTimeoutException; +import com.datastax.oss.driver.api.core.session.Request; +import com.datastax.oss.driver.api.core.session.throttling.RequestThrottler; +import com.datastax.oss.driver.api.core.session.throttling.Throttled; +import com.datastax.oss.driver.internal.core.adminrequest.ThrottledAdminRequestHandler; +import com.datastax.oss.driver.internal.core.adminrequest.UnexpectedResponseException; +import com.datastax.oss.driver.internal.core.channel.DriverChannel; +import com.datastax.oss.driver.internal.core.channel.ResponseCallback; +import com.datastax.oss.driver.internal.core.context.InternalDriverContext; +import com.datastax.oss.driver.internal.core.cql.Conversions; +import com.datastax.oss.driver.internal.core.cql.DefaultExecutionInfo; +import com.datastax.oss.driver.internal.core.metadata.DefaultNode; +import com.datastax.oss.driver.internal.core.metrics.NodeMetricUpdater; +import com.datastax.oss.driver.internal.core.metrics.SessionMetricUpdater; +import com.datastax.oss.driver.internal.core.session.DefaultSession; +import com.datastax.oss.driver.internal.core.session.RepreparePayload; +import com.datastax.oss.driver.internal.core.util.Loggers; +import com.datastax.oss.driver.internal.core.util.collection.SimpleQueryPlan; +import com.datastax.oss.driver.shaded.guava.common.annotations.VisibleForTesting; +import com.datastax.oss.protocol.internal.Frame; +import com.datastax.oss.protocol.internal.Message; +import com.datastax.oss.protocol.internal.ProtocolConstants; +import com.datastax.oss.protocol.internal.request.Prepare; +import com.datastax.oss.protocol.internal.response.Error; +import com.datastax.oss.protocol.internal.response.Result; +import com.datastax.oss.protocol.internal.response.error.Unprepared; +import com.datastax.oss.protocol.internal.response.result.Rows; +import com.datastax.oss.protocol.internal.response.result.Void; +import com.datastax.oss.protocol.internal.util.Bytes; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import io.netty.handler.codec.EncoderException; +import io.netty.util.Timeout; +import io.netty.util.Timer; +import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.GenericFutureListener; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.time.Duration; +import java.util.AbstractMap; +import java.util.ArrayDeque; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.ReentrantLock; +import net.jcip.annotations.GuardedBy; +import net.jcip.annotations.ThreadSafe; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Handles a request that supports multiple response messages (a.k.a. continuous paging request). + */ +@ThreadSafe +public abstract class ContinuousRequestHandlerBase + implements Throttled { + + private static final Logger LOG = LoggerFactory.getLogger(ContinuousRequestHandlerBase.class); + + protected final String logPrefix; + protected final StatementT initialStatement; + protected final DefaultSession session; + private final CqlIdentifier keyspace; + protected final InternalDriverContext context; + private final Queue queryPlan; + protected final RequestThrottler throttler; + private final boolean protocolBackpressureAvailable; + private final Timer timer; + private final SessionMetricUpdater sessionMetricUpdater; + private final boolean specExecEnabled; + private final SessionMetric clientTimeoutsMetric; + private final SessionMetric continuousRequestsMetric; + private final NodeMetric messagesMetric; + private final List scheduledExecutions; + + // The errors on the nodes that were already tried. + // We don't use a map because nodes can appear multiple times. + protected final List> errors = new CopyOnWriteArrayList<>(); + + /** + * The list of in-flight executions, one per node. Executions may be triggered by speculative + * executions or retries. An execution is added to this list when the write operation completes. + * It is removed from this list when the callback has done reading responses. + */ + private final List inFlightCallbacks = new CopyOnWriteArrayList<>(); + + /** The callback selected to stream results back to the client. */ + private final CompletableFuture chosenCallback = new CompletableFuture<>(); + + /** + * How many speculative executions are currently running (including the initial execution). We + * track this in order to know when to fail the request if all executions have reached the end of + * the query plan. + */ + private final AtomicInteger activeExecutionsCount = new AtomicInteger(0); + + /** + * How many speculative executions have started (excluding the initial execution), whether they + * have completed or not. We track this in order to fill execution info objects with this + * information. + */ + protected final AtomicInteger startedSpeculativeExecutionsCount = new AtomicInteger(0); + + // Set when the execution starts, and is never modified after. + private final long startTimeNanos; + private volatile Timeout globalTimeout; + + private final Class resultSetClass; + + public ContinuousRequestHandlerBase( + @NonNull StatementT statement, + @NonNull DefaultSession session, + @NonNull InternalDriverContext context, + @NonNull String sessionLogPrefix, + @NonNull Class resultSetClass, + boolean specExecEnabled, + SessionMetric clientTimeoutsMetric, + SessionMetric continuousRequestsMetric, + NodeMetric messagesMetric) { + this.resultSetClass = resultSetClass; + + ProtocolVersion protocolVersion = context.getProtocolVersion(); + if (!context + .getProtocolVersionRegistry() + .supports(protocolVersion, DseProtocolFeature.CONTINUOUS_PAGING)) { + throw new IllegalStateException( + "Cannot execute continuous paging requests with protocol version " + protocolVersion); + } + this.clientTimeoutsMetric = clientTimeoutsMetric; + this.continuousRequestsMetric = continuousRequestsMetric; + this.messagesMetric = messagesMetric; + this.logPrefix = sessionLogPrefix + "|" + this.hashCode(); + LOG.trace("[{}] Creating new continuous handler for request {}", logPrefix, statement); + this.initialStatement = statement; + this.session = session; + this.keyspace = session.getKeyspace().orElse(null); + this.context = context; + DriverExecutionProfile executionProfile = + Conversions.resolveExecutionProfile(statement, context); + this.queryPlan = + statement.getNode() != null + ? new SimpleQueryPlan(statement.getNode()) + : context + .getLoadBalancingPolicyWrapper() + .newQueryPlan(statement, executionProfile.getName(), session); + this.timer = context.getNettyOptions().getTimer(); + + this.protocolBackpressureAvailable = + protocolVersion.getCode() >= DseProtocolVersion.DSE_V2.getCode(); + this.throttler = context.getRequestThrottler(); + this.sessionMetricUpdater = session.getMetricUpdater(); + this.startTimeNanos = System.nanoTime(); + this.specExecEnabled = specExecEnabled; + this.scheduledExecutions = this.specExecEnabled ? new CopyOnWriteArrayList<>() : null; + } + + @NonNull + protected abstract Duration getGlobalTimeout(); + + @NonNull + protected abstract Duration getPageTimeout(@NonNull StatementT statement, int pageNumber); + + @NonNull + protected abstract Duration getReviseRequestTimeout(@NonNull StatementT statement); + + protected abstract int getMaxEnqueuedPages(@NonNull StatementT statement); + + protected abstract int getMaxPages(@NonNull StatementT statement); + + @NonNull + protected abstract Message getMessage(@NonNull StatementT statement); + + protected abstract boolean isTracingEnabled(@NonNull StatementT statement); + + @NonNull + protected abstract Map createPayload(@NonNull StatementT statement); + + @NonNull + protected abstract ResultSetT createEmptyResultSet(@NonNull ExecutionInfo executionInfo); + + protected abstract int pageNumber(@NonNull ResultSetT resultSet); + + @NonNull + protected abstract ResultSetT createResultSet( + @NonNull StatementT statement, + @NonNull Rows rows, + @NonNull ExecutionInfo executionInfo, + @NonNull ColumnDefinitions columnDefinitions) + throws IOException; + + // MAIN LIFECYCLE + + @Override + public void onThrottleReady(boolean wasDelayed) { + DriverExecutionProfile executionProfile = + Conversions.resolveExecutionProfile(initialStatement, context); + if (wasDelayed + // avoid call to nanoTime() if metric is disabled: + && sessionMetricUpdater.isEnabled( + DefaultSessionMetric.THROTTLING_DELAY, executionProfile.getName())) { + session + .getMetricUpdater() + .updateTimer( + DefaultSessionMetric.THROTTLING_DELAY, + executionProfile.getName(), + System.nanoTime() - startTimeNanos, + TimeUnit.NANOSECONDS); + } + activeExecutionsCount.incrementAndGet(); + sendRequest(initialStatement, null, 0, 0, specExecEnabled); + } + + @Override + public void onThrottleFailure(@NonNull RequestThrottlingException error) { + DriverExecutionProfile executionProfile = + Conversions.resolveExecutionProfile(initialStatement, context); + session + .getMetricUpdater() + .incrementCounter(DefaultSessionMetric.THROTTLING_ERRORS, executionProfile.getName()); + abortGlobalRequestOrChosenCallback(error); + } + + private void abortGlobalRequestOrChosenCallback(@NonNull Throwable error) { + if (!chosenCallback.completeExceptionally(error)) { + chosenCallback.thenAccept(callback -> callback.abort(error, false)); + } + } + + public CompletionStage handle() { + globalTimeout = scheduleGlobalTimeout(); + return fetchNextPage(); + } + + /** + * Builds the future that will get returned to the user from the initial execute call or a + * fetchNextPage() on the async API. + */ + public CompletionStage fetchNextPage() { + CompletableFuture result = new CompletableFuture<>(); + + // This is equivalent to + // `chosenCallback.thenCompose(NodeResponseCallback::dequeueOrCreatePending)`, except + // that we need to cancel `result` if `resultSetError` is a CancellationException. + chosenCallback.whenComplete( + (callback, callbackError) -> { + if (callbackError != null) { + result.completeExceptionally(callbackError); + } else { + callback + .dequeueOrCreatePending() + .whenComplete( + (resultSet, resultSetError) -> { + if (resultSetError != null) { + result.completeExceptionally(resultSetError); + } else { + result.complete(resultSet); + } + }); + } + }); + + // If the user cancels the future, propagate to our internal components + result.whenComplete( + (rs, t) -> { + if (t instanceof CancellationException) { + cancel(); + } + }); + + return result; + } + + /** + * Sends the initial request to the next available node. + * + * @param node if not null, it will be attempted first before the rest of the query plan. It + * happens only when we retry on the same host. + * @param currentExecutionIndex 0 for the initial execution, 1 for the first speculative one, etc. + * @param retryCount the number of times that the retry policy was invoked for this execution + * already (note that some internal retries don't go through the policy, and therefore don't + * increment this counter) + * @param scheduleSpeculativeExecution whether to schedule the next speculative execution + */ + private void sendRequest( + StatementT statement, + @Nullable Node node, + int currentExecutionIndex, + int retryCount, + boolean scheduleSpeculativeExecution) { + DriverChannel channel = null; + if (node == null || (channel = session.getChannel(node, logPrefix)) == null) { + while ((node = queryPlan.poll()) != null) { + channel = session.getChannel(node, logPrefix); + if (channel != null) { + break; + } else { + recordError(node, new NodeUnavailableException(node)); + } + } + } + if (channel == null) { + // We've reached the end of the query plan without finding any node to write to; abort the + // continuous paging session. + if (activeExecutionsCount.decrementAndGet() == 0) { + abortGlobalRequestOrChosenCallback(AllNodesFailedException.fromErrors(errors)); + } + } else if (!chosenCallback.isDone()) { + NodeResponseCallback nodeResponseCallback = + new NodeResponseCallback( + statement, + node, + channel, + currentExecutionIndex, + retryCount, + scheduleSpeculativeExecution, + logPrefix); + inFlightCallbacks.add(nodeResponseCallback); + channel + .write( + getMessage(statement), + isTracingEnabled(statement), + createPayload(statement), + nodeResponseCallback) + .addListener(nodeResponseCallback); + } + } + + private Timeout scheduleGlobalTimeout() { + Duration globalTimeout = getGlobalTimeout(); + if (globalTimeout.toNanos() <= 0) { + return null; + } + LOG.trace("[{}] Scheduling global timeout for pages in {}", logPrefix, globalTimeout); + return timer.newTimeout( + timeout -> + abortGlobalRequestOrChosenCallback( + new DriverTimeoutException("Query timed out after " + globalTimeout)), + globalTimeout.toNanos(), + TimeUnit.NANOSECONDS); + } + + /** + * Cancels the continuous paging request. + * + *

Called from user code, see {@link DefaultContinuousAsyncResultSet#cancel()}, or from a + * driver I/O thread. + */ + public void cancel() { + // If chosenCallback is already set, this is a no-op and the chosen callback will be handled by + // cancelScheduledTasks + chosenCallback.cancel(true); + + cancelScheduledTasks(null); + cancelGlobalTimeout(); + throttler.signalCancel(this); + } + + private void cancelGlobalTimeout() { + if (globalTimeout != null) { + globalTimeout.cancel(); + } + } + + /** + * Cancel all pending and scheduled executions, except the one passed as an argument to the + * method. + * + * @param toIgnore An optional execution to ignore (will not be cancelled). + */ + private void cancelScheduledTasks(@Nullable NodeResponseCallback toIgnore) { + if (scheduledExecutions != null) { + for (Timeout scheduledExecution : scheduledExecutions) { + scheduledExecution.cancel(); + } + } + for (NodeResponseCallback callback : inFlightCallbacks) { + if (toIgnore == null || toIgnore != callback) { + callback.cancel(); + } + } + } + + @VisibleForTesting + int getState() { + try { + return chosenCallback.get().getState(); + } catch (CancellationException e) { + // Happens if the test cancels before the callback was chosen + return NodeResponseCallback.STATE_FAILED; + } catch (InterruptedException | ExecutionException e) { + // We never interrupt or fail chosenCallback (other than canceling) + throw new AssertionError("Unexpected error", e); + } + } + + @VisibleForTesting + CompletableFuture getPendingResult() { + try { + return chosenCallback.get().getPendingResult(); + } catch (Exception e) { + // chosenCallback should always be complete by the time tests call this + throw new AssertionError("Expected callback to be chosen at this point"); + } + } + + private void recordError(@NonNull Node node, @NonNull Throwable error) { + errors.add(new AbstractMap.SimpleEntry<>(node, error)); + } + + /** + * Handles the interaction with a single node in the query plan. + * + *

An instance of this class is created each time we (re)try a node. The first callback that + * has something ready to enqueue will be allowed to stream results back to the client; the others + * will be cancelled. + */ + private class NodeResponseCallback + implements ResponseCallback, GenericFutureListener> { + + private final long messageStartTimeNanos = System.nanoTime(); + private final StatementT statement; + private final Node node; + private final DriverChannel channel; + // The identifier of the current execution (0 for the initial execution, 1 for the first + // speculative execution, etc.) + private final int executionIndex; + // How many times we've invoked the retry policy and it has returned a "retry" decision (0 for + // the first attempt of each execution). + private final String logPrefix; + private final boolean scheduleSpeculativeExecution; + + private final DriverExecutionProfile executionProfile; + + // Coordinates concurrent accesses between the client and I/O threads + private final ReentrantLock lock = new ReentrantLock(); + + // The page queue, storing responses that we have received and have not been consumed by the + // client yet. We instantiate it lazily to avoid unnecessary allocation; this is also used to + // check if the callback ever tried to enqueue something. + @GuardedBy("lock") + private Queue queue; + + // If the client requests a page and we can't serve it immediately (empty queue), then we create + // this future and have the client wait on it. Otherwise this field is null. + @GuardedBy("lock") + private CompletableFuture pendingResult; + + // How many pages were requested. This is the total number of pages requested from the + // beginning. + // It will be zero if the protocol does not support numPagesRequested (DSE_V1) + @GuardedBy("lock") + private int numPagesRequested; + + // An integer that represents the state of the continuous paging request: + // - if positive, it is the sequence number of the next expected page; + // - if negative, it is a terminal state, identified by the constants below. + @GuardedBy("lock") + private int state = 1; + + // Whether isLastResponse has returned true already + @GuardedBy("lock") + private boolean sawLastResponse; + + @GuardedBy("lock") + private boolean sentCancelRequest; + + private static final int STATE_FINISHED = -1; + private static final int STATE_FAILED = -2; + + @GuardedBy("lock") + private int streamId = -1; + + // These are set when the first page arrives, and are never modified after. + private volatile ColumnDefinitions columnDefinitions; + + private volatile Timeout pageTimeout; + + // How many times we've invoked the retry policy and it has returned a "retry" decision (0 for + // the first attempt, 1 for the first retry, etc.). + private final int retryCount; + + // SpeculativeExecution node metrics should be executed only for the first page (first + // invocation) + private final AtomicBoolean stopNodeMessageTimerReported = new AtomicBoolean(false); + private final AtomicBoolean nodeErrorReported = new AtomicBoolean(false); + private final AtomicBoolean nodeSuccessReported = new AtomicBoolean(false); + + public NodeResponseCallback( + StatementT statement, + Node node, + DriverChannel channel, + int executionIndex, + int retryCount, + boolean scheduleSpeculativeExecution, + String logPrefix) { + this.statement = statement; + this.node = node; + this.channel = channel; + this.executionIndex = executionIndex; + this.retryCount = retryCount; + this.scheduleSpeculativeExecution = scheduleSpeculativeExecution; + this.logPrefix = logPrefix + "|" + executionIndex; + this.executionProfile = Conversions.resolveExecutionProfile(statement, context); + } + + @Override + public void onStreamIdAssigned(int streamId) { + LOG.trace("[{}] Assigned streamId {} on node {}", logPrefix, streamId, node); + lock.lock(); + try { + this.streamId = streamId; + if (state < 0) { + // This happens if we were cancelled before getting the stream id, we have a request in + // flight that needs to be cancelled + releaseStreamId(); + } + } finally { + lock.unlock(); + } + } + + @Override + public boolean isLastResponse(@NonNull Frame responseFrame) { + lock.lock(); + try { + Message message = responseFrame.message; + boolean isLastResponse; + + if (sentCancelRequest) { + // The only response we accept is the SERVER_ERROR triggered by a successful cancellation. + // Otherwise we risk releasing and reusing the stream id while the cancel request is still + // in flight, and it might end up cancelling an unrelated request. + // Note that there is a chance that the request ends normally right after we send the + // cancel request. In that case this method never returns true and the stream id will + // remain orphaned forever. This should be very rare so this is acceptable. + if (message instanceof Error) { + Error error = (Error) message; + isLastResponse = + (error.code == ProtocolConstants.ErrorCode.SERVER_ERROR) + && error.message.contains("Session cancelled by the user"); + } else { + isLastResponse = false; + } + } else if (message instanceof Rows) { + Rows rows = (Rows) message; + DseRowsMetadata metadata = (DseRowsMetadata) rows.getMetadata(); + isLastResponse = metadata.isLastContinuousPage; + } else { + isLastResponse = message instanceof Error; + } + + if (isLastResponse) { + sawLastResponse = true; + } + return isLastResponse; + } finally { + lock.unlock(); + } + } + + /** + * Invoked when the write from {@link #sendRequest} completes. + * + * @param future The future representing the outcome of the write operation. + */ + @Override + public void operationComplete(@NonNull Future future) { + if (!future.isSuccess()) { + Throwable error = future.cause(); + if (error instanceof EncoderException + && error.getCause() instanceof FrameTooLongException) { + trackNodeError(node, error.getCause()); + lock.lock(); + try { + abort(error.getCause(), false); + } finally { + lock.unlock(); + } + } else { + LOG.trace( + "[{}] Failed to send request on {}, trying next node (cause: {})", + logPrefix, + channel, + error); + ((DefaultNode) node) + .getMetricUpdater() + .incrementCounter(DefaultNodeMetric.UNSENT_REQUESTS, executionProfile.getName()); + recordError(node, error); + trackNodeError(node, error.getCause()); + sendRequest(statement, null, executionIndex, retryCount, scheduleSpeculativeExecution); + } + } else { + LOG.trace("[{}] Request sent on {}", logPrefix, channel); + if (scheduleSpeculativeExecution + && Conversions.resolveIdempotence(statement, executionProfile)) { + int nextExecution = executionIndex + 1; + // Note that `node` is the first node of the execution, it might not be the "slow" one + // if there were retries, but in practice retries are rare. + long nextDelay = + Conversions.resolveSpeculativeExecutionPolicy(context, executionProfile) + .nextExecution(node, keyspace, statement, nextExecution); + if (nextDelay >= 0) { + scheduleSpeculativeExecution(nextExecution, nextDelay); + } else { + LOG.trace( + "[{}] Speculative execution policy returned {}, no next execution", + logPrefix, + nextDelay); + } + } + pageTimeout = schedulePageTimeout(1); + } + } + + private void scheduleSpeculativeExecution(int nextExecutionIndex, long delay) { + LOG.trace( + "[{}] Scheduling speculative execution {} in {} ms", + logPrefix, + nextExecutionIndex, + delay); + try { + scheduledExecutions.add( + timer.newTimeout( + (Timeout timeout) -> { + if (!chosenCallback.isDone()) { + LOG.trace( + "[{}] Starting speculative execution {}", logPrefix, nextExecutionIndex); + activeExecutionsCount.incrementAndGet(); + startedSpeculativeExecutionsCount.incrementAndGet(); + NodeMetricUpdater nodeMetricUpdater = ((DefaultNode) node).getMetricUpdater(); + if (nodeMetricUpdater.isEnabled( + DefaultNodeMetric.SPECULATIVE_EXECUTIONS, executionProfile.getName())) { + nodeMetricUpdater.incrementCounter( + DefaultNodeMetric.SPECULATIVE_EXECUTIONS, executionProfile.getName()); + } + sendRequest(statement, null, nextExecutionIndex, 0, true); + } + }, + delay, + TimeUnit.MILLISECONDS)); + } catch (IllegalStateException e) { + logTimeoutSchedulingError(e); + } + } + + private Timeout schedulePageTimeout(int expectedPage) { + if (expectedPage < 0) { + return null; + } + Duration timeout = getPageTimeout(statement, expectedPage); + if (timeout.toNanos() <= 0) { + return null; + } + LOG.trace("[{}] Scheduling timeout for page {} in {}", logPrefix, expectedPage, timeout); + return timer.newTimeout( + t -> onPageTimeout(expectedPage), timeout.toNanos(), TimeUnit.NANOSECONDS); + } + + private void onPageTimeout(int expectedPage) { + lock.lock(); + try { + if (state == expectedPage) { + abort( + new DriverTimeoutException( + String.format("Timed out waiting for page %d", expectedPage)), + false); + } else { + // Ignore timeout if the request has moved on in the interim. + LOG.trace( + "[{}] Timeout fired for page {} but query already at state {}, skipping", + logPrefix, + expectedPage, + state); + } + } finally { + lock.unlock(); + } + } + + /** + * Invoked when a continuous paging response is received, either a successful or failed one. + * + *

Delegates further processing to appropriate methods: {@link #processResultResponse(Result, + * Frame)} if the response was successful, or {@link #processErrorResponse(Error)} if it wasn't. + * + * @param response the received {@link Frame}. + */ + @Override + public void onResponse(@NonNull Frame response) { + stopNodeMessageTimer(); + cancelTimeout(pageTimeout); + lock.lock(); + try { + if (state < 0) { + LOG.trace("[{}] Got result but the request has been cancelled, ignoring", logPrefix); + return; + } + try { + Message responseMessage = response.message; + if (responseMessage instanceof Result) { + LOG.trace("[{}] Got result", logPrefix); + processResultResponse((Result) responseMessage, response); + } else if (responseMessage instanceof Error) { + LOG.trace("[{}] Got error response", logPrefix); + processErrorResponse((Error) responseMessage); + } else { + IllegalStateException error = + new IllegalStateException("Unexpected response " + responseMessage); + trackNodeError(node, error); + abort(error, false); + } + } catch (Throwable t) { + trackNodeError(node, t); + abort(t, false); + } + } finally { + lock.unlock(); + } + } + + /** + * Invoked when a continuous paging request hits an unexpected error. + * + *

Delegates further processing to to the retry policy ({@link + * #processRetryVerdict(RetryVerdict, Throwable)}. + * + * @param error the error encountered, usually a network problem. + */ + @Override + public void onFailure(@NonNull Throwable error) { + cancelTimeout(pageTimeout); + LOG.trace(String.format("[%s] Request failure", logPrefix), error); + RetryVerdict verdict; + if (!Conversions.resolveIdempotence(statement, executionProfile) + || error instanceof FrameTooLongException) { + verdict = RetryVerdict.RETHROW; + } else { + try { + RetryPolicy retryPolicy = Conversions.resolveRetryPolicy(context, executionProfile); + verdict = retryPolicy.onRequestAbortedVerdict(statement, error, retryCount); + } catch (Throwable cause) { + abort( + new IllegalStateException("Unexpected error while invoking the retry policy", cause), + false); + return; + } + } + updateErrorMetrics( + ((DefaultNode) node).getMetricUpdater(), + verdict, + DefaultNodeMetric.ABORTED_REQUESTS, + DefaultNodeMetric.RETRIES_ON_ABORTED, + DefaultNodeMetric.IGNORES_ON_ABORTED); + lock.lock(); + try { + processRetryVerdict(verdict, error); + } finally { + lock.unlock(); + } + } + + // PROCESSING METHODS + + /** + * Processes a new result response, creating the corresponding {@link ResultSetT} object and + * then enqueuing it or serving it directly to the user if he was waiting for it. + * + * @param result the result to process. It is normally a {@link Rows} object, but may be a + * {@link Void} object if the retry policy decided to ignore an error. + * @param frame the {@link Frame} (used to create the {@link ExecutionInfo} the first time). + */ + @SuppressWarnings("GuardedBy") // this method is only called with the lock held + private void processResultResponse(@NonNull Result result, @Nullable Frame frame) { + assert lock.isHeldByCurrentThread(); + try { + ExecutionInfo executionInfo = createExecutionInfo(result, frame); + if (result instanceof Rows) { + DseRowsMetadata rowsMetadata = (DseRowsMetadata) ((Rows) result).getMetadata(); + if (columnDefinitions == null) { + // Contrary to ROWS responses from regular queries, + // the first page always includes metadata so we use this + // regardless of whether or not the query was from a prepared statement. + columnDefinitions = Conversions.toColumnDefinitions(rowsMetadata, context); + } + int pageNumber = rowsMetadata.continuousPageNumber; + int currentPage = state; + if (pageNumber != currentPage) { + abort( + new IllegalStateException( + String.format( + "Received page %d but was expecting %d", pageNumber, currentPage)), + false); + } else { + int pageSize = ((Rows) result).getData().size(); + ResultSetT resultSet = + createResultSet(statement, (Rows) result, executionInfo, columnDefinitions); + if (rowsMetadata.isLastContinuousPage) { + LOG.trace("[{}] Received last page ({} - {} rows)", logPrefix, pageNumber, pageSize); + state = STATE_FINISHED; + reenableAutoReadIfNeeded(); + enqueueOrCompletePending(resultSet); + stopGlobalRequestTimer(); + cancelTimeout(globalTimeout); + } else { + LOG.trace("[{}] Received page {} ({} rows)", logPrefix, pageNumber, pageSize); + if (currentPage > 0) { + state = currentPage + 1; + } + enqueueOrCompletePending(resultSet); + } + } + } else { + // Void responses happen only when the retry decision is ignore. + assert result instanceof Void; + ResultSetT resultSet = createEmptyResultSet(executionInfo); + LOG.trace( + "[{}] Continuous paging interrupted by retry policy decision to ignore error", + logPrefix); + state = STATE_FINISHED; + reenableAutoReadIfNeeded(); + enqueueOrCompletePending(resultSet); + stopGlobalRequestTimer(); + cancelTimeout(globalTimeout); + } + } catch (Throwable error) { + abort(error, false); + } + } + + /** + * Processes an unsuccessful response. + * + *

Depending on the error, may trigger: + * + *

    + *
  1. a re-prepare cycle, see {@link #processUnprepared(Unprepared)}; + *
  2. an immediate retry on the next host, bypassing the retry policy, if the host was + * bootstrapping; + *
  3. an immediate abortion if the error is unrecoverable; + *
  4. further processing if the error is recoverable, see {@link + * #processRecoverableError(CoordinatorException)} + *
+ * + * @param errorMessage the error message received. + */ + @SuppressWarnings("GuardedBy") // this method is only called with the lock held + private void processErrorResponse(@NonNull Error errorMessage) { + assert lock.isHeldByCurrentThread(); + if (errorMessage instanceof Unprepared) { + processUnprepared((Unprepared) errorMessage); + } else { + CoordinatorException error = DseConversions.toThrowable(node, errorMessage, context); + if (error instanceof BootstrappingException) { + LOG.trace("[{}] {} is bootstrapping, trying next node", logPrefix, node); + recordError(node, error); + trackNodeError(node, error); + sendRequest(statement, null, executionIndex, retryCount, false); + } else if (error instanceof QueryValidationException + || error instanceof FunctionFailureException + || error instanceof ProtocolError + || state > 1) { + // we only process recoverable errors for the first page, + // errors on subsequent pages will always trigger an immediate abortion + LOG.trace("[{}] Unrecoverable error, rethrowing", logPrefix); + NodeMetricUpdater metricUpdater = ((DefaultNode) node).getMetricUpdater(); + metricUpdater.incrementCounter( + DefaultNodeMetric.OTHER_ERRORS, executionProfile.getName()); + trackNodeError(node, error); + abort(error, true); + } else { + try { + processRecoverableError(error); + } catch (Throwable cause) { + abort(cause, false); + } + } + } + } + + /** + * Processes a recoverable error. + * + *

In most cases, delegates to the retry policy and its decision, see {@link + * #processRetryVerdict(RetryVerdict, Throwable)}. + * + * @param error the recoverable error. + */ + private void processRecoverableError(@NonNull CoordinatorException error) { + assert lock.isHeldByCurrentThread(); + NodeMetricUpdater metricUpdater = ((DefaultNode) node).getMetricUpdater(); + RetryVerdict verdict; + RetryPolicy retryPolicy = Conversions.resolveRetryPolicy(context, executionProfile); + if (error instanceof ReadTimeoutException) { + ReadTimeoutException readTimeout = (ReadTimeoutException) error; + verdict = + retryPolicy.onReadTimeoutVerdict( + statement, + readTimeout.getConsistencyLevel(), + readTimeout.getBlockFor(), + readTimeout.getReceived(), + readTimeout.wasDataPresent(), + retryCount); + updateErrorMetrics( + metricUpdater, + verdict, + DefaultNodeMetric.READ_TIMEOUTS, + DefaultNodeMetric.RETRIES_ON_READ_TIMEOUT, + DefaultNodeMetric.IGNORES_ON_READ_TIMEOUT); + } else if (error instanceof WriteTimeoutException) { + WriteTimeoutException writeTimeout = (WriteTimeoutException) error; + if (Conversions.resolveIdempotence(statement, executionProfile)) { + verdict = + retryPolicy.onWriteTimeoutVerdict( + statement, + writeTimeout.getConsistencyLevel(), + writeTimeout.getWriteType(), + writeTimeout.getBlockFor(), + writeTimeout.getReceived(), + retryCount); + } else { + verdict = RetryVerdict.RETHROW; + } + updateErrorMetrics( + metricUpdater, + verdict, + DefaultNodeMetric.WRITE_TIMEOUTS, + DefaultNodeMetric.RETRIES_ON_WRITE_TIMEOUT, + DefaultNodeMetric.IGNORES_ON_WRITE_TIMEOUT); + } else if (error instanceof UnavailableException) { + UnavailableException unavailable = (UnavailableException) error; + verdict = + retryPolicy.onUnavailableVerdict( + statement, + unavailable.getConsistencyLevel(), + unavailable.getRequired(), + unavailable.getAlive(), + retryCount); + updateErrorMetrics( + metricUpdater, + verdict, + DefaultNodeMetric.UNAVAILABLES, + DefaultNodeMetric.RETRIES_ON_UNAVAILABLE, + DefaultNodeMetric.IGNORES_ON_UNAVAILABLE); + } else { + verdict = + Conversions.resolveIdempotence(statement, executionProfile) + ? retryPolicy.onErrorResponseVerdict(statement, error, retryCount) + : RetryVerdict.RETHROW; + updateErrorMetrics( + metricUpdater, + verdict, + DefaultNodeMetric.OTHER_ERRORS, + DefaultNodeMetric.RETRIES_ON_OTHER_ERROR, + DefaultNodeMetric.IGNORES_ON_OTHER_ERROR); + } + processRetryVerdict(verdict, error); + } + + /** + * Processes an {@link Unprepared} error by re-preparing then retrying on the same host. + * + * @param errorMessage the unprepared error message. + */ + @SuppressWarnings("GuardedBy") // this method is only called with the lock held + private void processUnprepared(@NonNull Unprepared errorMessage) { + assert lock.isHeldByCurrentThread(); + ByteBuffer idToReprepare = ByteBuffer.wrap(errorMessage.id); + LOG.trace( + "[{}] Statement {} is not prepared on {}, re-preparing", + logPrefix, + Bytes.toHexString(idToReprepare), + node); + RepreparePayload repreparePayload = session.getRepreparePayloads().get(idToReprepare); + if (repreparePayload == null) { + throw new IllegalStateException( + String.format( + "Tried to execute unprepared query %s but we don't have the data to re-prepare it", + Bytes.toHexString(idToReprepare))); + } + Prepare prepare = repreparePayload.toMessage(); + Duration timeout = executionProfile.getDuration(DefaultDriverOption.REQUEST_TIMEOUT); + ThrottledAdminRequestHandler.prepare( + channel, + true, + prepare, + repreparePayload.customPayload, + timeout, + throttler, + sessionMetricUpdater, + logPrefix) + .start() + .whenComplete( + (repreparedId, exception) -> { + // If we run into an unrecoverable error, surface it to the client instead of + // retrying + Throwable fatalError = null; + if (exception == null) { + if (!repreparedId.equals(idToReprepare)) { + IllegalStateException illegalStateException = + new IllegalStateException( + String.format( + "ID mismatch while trying to reprepare (expected %s, got %s). " + + "This prepared statement won't work anymore. " + + "This usually happens when you run a 'USE...' query after " + + "the statement was prepared.", + Bytes.toHexString(idToReprepare), Bytes.toHexString(repreparedId))); + trackNodeError(node, illegalStateException); + fatalError = illegalStateException; + } else { + LOG.trace( + "[{}] Re-prepare successful, retrying on the same node ({})", + logPrefix, + node); + sendRequest(statement, node, executionIndex, retryCount, false); + } + } else { + if (exception instanceof UnexpectedResponseException) { + Message prepareErrorMessage = ((UnexpectedResponseException) exception).message; + if (prepareErrorMessage instanceof Error) { + CoordinatorException prepareError = + DseConversions.toThrowable(node, (Error) prepareErrorMessage, context); + if (prepareError instanceof QueryValidationException + || prepareError instanceof FunctionFailureException + || prepareError instanceof ProtocolError) { + LOG.trace("[{}] Unrecoverable error on re-prepare, rethrowing", logPrefix); + trackNodeError(node, prepareError); + fatalError = prepareError; + } + } + } else if (exception instanceof RequestThrottlingException) { + trackNodeError(node, exception); + fatalError = exception; + } + if (fatalError == null) { + LOG.trace("[{}] Re-prepare failed, trying next node", logPrefix); + recordError(node, exception); + trackNodeError(node, exception); + sendRequest(statement, null, executionIndex, retryCount, false); + } + } + if (fatalError != null) { + lock.lock(); + try { + abort(fatalError, true); + } finally { + lock.unlock(); + } + } + }); + } + + /** + * Processes the retry decision by triggering a retry, aborting or ignoring; also records the + * failures for further access. + * + * @param verdict the verdict to process. + * @param error the original error. + */ + private void processRetryVerdict(@NonNull RetryVerdict verdict, @NonNull Throwable error) { + assert lock.isHeldByCurrentThread(); + LOG.trace("[{}] Processing retry decision {}", logPrefix, verdict); + switch (verdict.getRetryDecision()) { + case RETRY_SAME: + recordError(node, error); + trackNodeError(node, error); + sendRequest( + verdict.getRetryRequest(statement), node, executionIndex, retryCount + 1, false); + break; + case RETRY_NEXT: + recordError(node, error); + trackNodeError(node, error); + sendRequest( + verdict.getRetryRequest(statement), null, executionIndex, retryCount + 1, false); + break; + case RETHROW: + trackNodeError(node, error); + abort(error, true); + break; + case IGNORE: + processResultResponse(Void.INSTANCE, null); + break; + } + } + + // PAGE HANDLING + + /** + * Enqueues a response or, if the client was already waiting for it, completes the pending + * future. + * + *

Guarded by {@link #lock}. + * + * @param pageOrError the next page, or an error. + */ + @SuppressWarnings("GuardedBy") // this method is only called with the lock held + private void enqueueOrCompletePending(@NonNull Object pageOrError) { + assert lock.isHeldByCurrentThread(); + + if (queue == null) { + // This is the first time this callback tries to stream something back to the client, check + // if it can be selected + if (!chosenCallback.complete(this)) { + if (LOG.isTraceEnabled()) { + LOG.trace( + "[{}] Trying to enqueue {} but another callback was already chosen, aborting", + logPrefix, + asTraceString(pageOrError)); + } + // Discard the data, this callback will be canceled shortly since the chosen callback + // invoked cancelScheduledTasks + return; + } + + queue = new ArrayDeque<>(getMaxEnqueuedPages(statement)); + numPagesRequested = protocolBackpressureAvailable ? getMaxEnqueuedPages(statement) : 0; + cancelScheduledTasks(this); + } + + if (pendingResult != null) { + if (LOG.isTraceEnabled()) { + LOG.trace( + "[{}] Client was waiting on empty queue, completing with {}", + logPrefix, + asTraceString(pageOrError)); + } + CompletableFuture tmp = pendingResult; + // null out pendingResult before completing it because its completion + // may trigger a call to fetchNextPage -> dequeueOrCreatePending, + // which expects pendingResult to be null. + pendingResult = null; + completeResultSetFuture(tmp, pageOrError); + } else { + if (LOG.isTraceEnabled()) { + LOG.trace("[{}] Enqueuing {}", logPrefix, asTraceString(pageOrError)); + } + queue.add(pageOrError); + // Backpressure without protocol support: if the queue grows too large, + // disable auto-read so that the channel eventually becomes + // non-writable on the server side (causing it to back off for a while) + if (!protocolBackpressureAvailable + && queue.size() == getMaxEnqueuedPages(statement) + && state > 0) { + LOG.trace( + "[{}] Exceeded {} queued response pages, disabling auto-read", + logPrefix, + queue.size()); + channel.config().setAutoRead(false); + } + } + } + + /** + * Dequeue a response or, if the queue is empty, create the future that will get notified of the + * next response, when it arrives. + * + *

Called from user code, see {@link ContinuousAsyncResultSet#fetchNextPage()}. + * + * @return the next page's future; never null. + */ + @NonNull + public CompletableFuture dequeueOrCreatePending() { + lock.lock(); + try { + // If the client was already waiting for a page, there's no way it can call this method + // again + // (this is guaranteed by our public API because in order to ask for the next page, + // you need the reference to the previous page). + assert pendingResult == null; + + Object head = null; + if (queue != null) { + head = queue.poll(); + if (!protocolBackpressureAvailable + && head != null + && queue.size() == getMaxEnqueuedPages(statement) - 1) { + LOG.trace( + "[{}] Back to {} queued response pages, re-enabling auto-read", + logPrefix, + queue.size()); + channel.config().setAutoRead(true); + } + maybeRequestMore(); + } + + if (head != null) { + if (state == STATE_FAILED && !(head instanceof Throwable)) { + LOG.trace( + "[{}] Client requested next page on cancelled queue, discarding page and returning cancelled future", + logPrefix); + return cancelledResultSetFuture(); + } else { + if (LOG.isTraceEnabled()) { + LOG.trace( + "[{}] Client requested next page on non-empty queue, returning immediate future of {}", + logPrefix, + asTraceString(head)); + } + return immediateResultSetFuture(head); + } + } else { + if (state == STATE_FAILED) { + LOG.trace( + "[{}] Client requested next page on cancelled empty queue, returning cancelled future", + logPrefix); + return cancelledResultSetFuture(); + } else { + LOG.trace( + "[{}] Client requested next page but queue is empty, installing future", logPrefix); + pendingResult = new CompletableFuture<>(); + // Only schedule a timeout if we're past the first page (the first page's timeout is + // handled in sendRequest). + if (state > 1) { + pageTimeout = schedulePageTimeout(state); + // Note: each new timeout is cancelled when the next response arrives, see + // onResponse(Frame). + } + return pendingResult; + } + } + } finally { + lock.unlock(); + } + } + + /** + * If the total number of results in the queue and in-flight (requested - received) is less than + * half the queue size, then request more pages, unless the {@link #state} is failed, we're + * still waiting for the first page (so maybe still throttled or in the middle of a retry), or + * we don't support backpressure at the protocol level. + */ + @SuppressWarnings("GuardedBy") + private void maybeRequestMore() { + assert lock.isHeldByCurrentThread(); + if (state < 2 || streamId == -1 || !protocolBackpressureAvailable) { + return; + } + // if we have already requested more than the client needs, then no need to request some more + int maxPages = getMaxPages(statement); + if (maxPages > 0 && numPagesRequested >= maxPages) { + return; + } + // the pages received so far, which is the state minus one + int received = state - 1; + int requested = numPagesRequested; + // the pages that fit in the queue, which is the queue free space minus the requests in flight + int freeSpace = getMaxEnqueuedPages(statement) - queue.size(); + int inFlight = requested - received; + int numPagesFittingInQueue = freeSpace - inFlight; + if (numPagesFittingInQueue > 0 + && numPagesFittingInQueue >= getMaxEnqueuedPages(statement) / 2) { + LOG.trace("[{}] Requesting more {} pages", logPrefix, numPagesFittingInQueue); + numPagesRequested = requested + numPagesFittingInQueue; + sendMorePagesRequest(numPagesFittingInQueue); + } + } + + /** + * Sends a request for more pages (a.k.a. backpressure request). + * + * @param nextPages the number of extra pages to request. + */ + @SuppressWarnings("GuardedBy") + private void sendMorePagesRequest(int nextPages) { + assert lock.isHeldByCurrentThread(); + assert channel != null : "expected valid connection in order to request more pages"; + assert protocolBackpressureAvailable; + assert streamId != -1; + + LOG.trace("[{}] Sending request for more pages", logPrefix); + ThrottledAdminRequestHandler.query( + channel, + true, + Revise.requestMoreContinuousPages(streamId, nextPages), + statement.getCustomPayload(), + getReviseRequestTimeout(statement), + throttler, + session.getMetricUpdater(), + logPrefix, + "request " + nextPages + " more pages for id " + streamId) + .start() + .handle( + (result, error) -> { + if (error != null) { + Loggers.warnWithException( + LOG, "[{}] Error requesting more pages, aborting.", logPrefix, error); + lock.lock(); + try { + // Set fromServer to false because we want the callback to still cancel the + // session if possible or else the server will wait on a timeout. + abort(error, false); + } finally { + lock.unlock(); + } + } + return null; + }); + } + + /** Cancels the given timeout, if non null. */ + private void cancelTimeout(Timeout timeout) { + if (timeout != null) { + LOG.trace("[{}] Cancelling timeout", logPrefix); + timeout.cancel(); + } + } + + // CANCELLATION + + public void cancel() { + lock.lock(); + try { + if (state < 0) { + return; + } else { + LOG.trace( + "[{}] Cancelling continuous paging session with state {} on node {}", + logPrefix, + state, + node); + state = STATE_FAILED; + if (pendingResult != null) { + pendingResult.cancel(true); + } + releaseStreamId(); + } + } finally { + lock.unlock(); + } + reenableAutoReadIfNeeded(); + } + + @SuppressWarnings("GuardedBy") + private void releaseStreamId() { + assert lock.isHeldByCurrentThread(); + // If we saw the last response already, InFlightHandler will release the id so no need to + // cancel explicitly + if (streamId >= 0 && !sawLastResponse && !channel.closeFuture().isDone()) { + // This orphans the stream id, but it will still be held until we see the last response: + channel.cancel(this); + // This tells the server to stop streaming, and send a terminal response: + sendCancelRequest(); + } + } + + @SuppressWarnings("GuardedBy") + private void sendCancelRequest() { + assert lock.isHeldByCurrentThread(); + LOG.trace("[{}] Sending cancel request", logPrefix); + ThrottledAdminRequestHandler.query( + channel, + true, + Revise.cancelContinuousPaging(streamId), + statement.getCustomPayload(), + getReviseRequestTimeout(statement), + throttler, + session.getMetricUpdater(), + logPrefix, + "cancel request") + .start() + .handle( + (result, error) -> { + if (error != null) { + Loggers.warnWithException( + LOG, + "[{}] Error sending cancel request. " + + "This is not critical (the request will eventually time out server-side).", + logPrefix, + error); + } else { + LOG.trace("[{}] Continuous paging session cancelled successfully", logPrefix); + } + return null; + }); + sentCancelRequest = true; + } + + // TERMINATION + + private void reenableAutoReadIfNeeded() { + // Make sure we don't leave the channel unreadable + LOG.trace("[{}] Re-enabling auto-read", logPrefix); + if (!protocolBackpressureAvailable) { + channel.config().setAutoRead(true); + } + } + + // ERROR HANDLING + + private void trackNodeError(@NonNull Node node, @NonNull Throwable error) { + if (nodeErrorReported.compareAndSet(false, true)) { + long latencyNanos = System.nanoTime() - this.messageStartTimeNanos; + context + .getRequestTracker() + .onNodeError(this.statement, error, latencyNanos, executionProfile, node, logPrefix); + } + } + + /** + * Aborts the continuous paging session due to an error that can be either from the server or + * the client. + * + * @param error the error that causes the abortion. + * @param fromServer whether the error was triggered by the coordinator or by the driver. + */ + @SuppressWarnings("GuardedBy") // this method is only called with the lock held + private void abort(@NonNull Throwable error, boolean fromServer) { + assert lock.isHeldByCurrentThread(); + LOG.trace( + "[{}] Aborting due to {} ({})", + logPrefix, + error.getClass().getSimpleName(), + error.getMessage()); + if (channel == null) { + // This only happens when sending the initial request, if no host was available + // or if the iterator returned by the LBP threw an exception. + // In either case the write was not even attempted, and + // we set the state right now. + enqueueOrCompletePending(error); + state = STATE_FAILED; + } else if (state > 0) { + enqueueOrCompletePending(error); + if (fromServer) { + // We can safely assume the server won't send any more responses, + // so set the state and call release() right now. + state = STATE_FAILED; + reenableAutoReadIfNeeded(); + } else { + // attempt to cancel first, i.e. ask server to stop sending responses, + // and only then release. + cancel(); + } + } + stopGlobalRequestTimer(); + cancelTimeout(globalTimeout); + } + + // METRICS + + private void stopNodeMessageTimer() { + if (stopNodeMessageTimerReported.compareAndSet(false, true)) { + ((DefaultNode) node) + .getMetricUpdater() + .updateTimer( + messagesMetric, + executionProfile.getName(), + System.nanoTime() - messageStartTimeNanos, + TimeUnit.NANOSECONDS); + } + } + + private void stopGlobalRequestTimer() { + session + .getMetricUpdater() + .updateTimer( + continuousRequestsMetric, + null, + System.nanoTime() - startTimeNanos, + TimeUnit.NANOSECONDS); + } + + private void updateErrorMetrics( + @NonNull NodeMetricUpdater metricUpdater, + @NonNull RetryVerdict verdict, + @NonNull DefaultNodeMetric error, + @NonNull DefaultNodeMetric retriesOnError, + @NonNull DefaultNodeMetric ignoresOnError) { + metricUpdater.incrementCounter(error, executionProfile.getName()); + switch (verdict.getRetryDecision()) { + case RETRY_SAME: + case RETRY_NEXT: + metricUpdater.incrementCounter(DefaultNodeMetric.RETRIES, executionProfile.getName()); + metricUpdater.incrementCounter(retriesOnError, executionProfile.getName()); + break; + case IGNORE: + metricUpdater.incrementCounter(DefaultNodeMetric.IGNORES, executionProfile.getName()); + metricUpdater.incrementCounter(ignoresOnError, executionProfile.getName()); + break; + case RETHROW: + // nothing do do + } + } + + // UTILITY METHODS + + @NonNull + private CompletableFuture immediateResultSetFuture(@NonNull Object pageOrError) { + CompletableFuture future = new CompletableFuture<>(); + completeResultSetFuture(future, pageOrError); + return future; + } + + @NonNull + private CompletableFuture cancelledResultSetFuture() { + return immediateResultSetFuture( + new CancellationException( + "Can't get more results because the continuous query has failed already. " + + "Most likely this is because the query was cancelled")); + } + + private void completeResultSetFuture( + @NonNull CompletableFuture future, @NonNull Object pageOrError) { + long now = System.nanoTime(); + long totalLatencyNanos = now - startTimeNanos; + long nodeLatencyNanos = now - messageStartTimeNanos; + if (resultSetClass.isInstance(pageOrError)) { + if (future.complete(resultSetClass.cast(pageOrError))) { + throttler.signalSuccess(ContinuousRequestHandlerBase.this); + if (nodeSuccessReported.compareAndSet(false, true)) { + context + .getRequestTracker() + .onNodeSuccess(statement, nodeLatencyNanos, executionProfile, node, logPrefix); + } + context + .getRequestTracker() + .onSuccess(statement, totalLatencyNanos, executionProfile, node, logPrefix); + } + } else { + Throwable error = (Throwable) pageOrError; + if (future.completeExceptionally(error)) { + context + .getRequestTracker() + .onError(statement, error, totalLatencyNanos, executionProfile, node, logPrefix); + if (error instanceof DriverTimeoutException) { + throttler.signalTimeout(ContinuousRequestHandlerBase.this); + session + .getMetricUpdater() + .incrementCounter(clientTimeoutsMetric, executionProfile.getName()); + } else if (!(error instanceof RequestThrottlingException)) { + throttler.signalError(ContinuousRequestHandlerBase.this, error); + } + } + } + } + + @NonNull + private ExecutionInfo createExecutionInfo(@NonNull Result result, @Nullable Frame response) { + ByteBuffer pagingState = + result instanceof Rows ? ((Rows) result).getMetadata().pagingState : null; + return new DefaultExecutionInfo( + statement, + node, + startedSpeculativeExecutionsCount.get(), + executionIndex, + errors, + pagingState, + response, + true, + session, + context, + executionProfile); + } + + private void logTimeoutSchedulingError(IllegalStateException timeoutError) { + // If we're racing with session shutdown, the timer might be stopped already. We don't want + // to schedule more executions anyway, so swallow the error. + if (!"cannot be started once stopped".equals(timeoutError.getMessage())) { + Loggers.warnWithException( + LOG, "[{}] Error while scheduling timeout", logPrefix, timeoutError); + } + } + + @NonNull + private String asTraceString(@NonNull Object pageOrError) { + return resultSetClass.isInstance(pageOrError) + ? "page " + pageNumber(resultSetClass.cast(pageOrError)) + : ((Exception) pageOrError).getClass().getSimpleName(); + } + + private int getState() { + lock.lock(); + try { + return state; + } finally { + lock.unlock(); + } + } + + private CompletableFuture getPendingResult() { + lock.lock(); + try { + return pendingResult; + } finally { + lock.unlock(); + } + } + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/cql/continuous/DefaultContinuousAsyncResultSet.java b/core/src/main/java/com/datastax/dse/driver/internal/core/cql/continuous/DefaultContinuousAsyncResultSet.java new file mode 100644 index 00000000000..8562fde5905 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/cql/continuous/DefaultContinuousAsyncResultSet.java @@ -0,0 +1,169 @@ +/* + * 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 com.datastax.dse.driver.internal.core.cql.continuous; + +import com.datastax.dse.driver.api.core.cql.continuous.ContinuousAsyncResultSet; +import com.datastax.oss.driver.api.core.cql.ColumnDefinitions; +import com.datastax.oss.driver.api.core.cql.ExecutionInfo; +import com.datastax.oss.driver.api.core.cql.Row; +import com.datastax.oss.driver.internal.core.cql.EmptyColumnDefinitions; +import com.datastax.oss.driver.internal.core.util.CountingIterator; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.Collections; +import java.util.concurrent.CompletionStage; +import net.jcip.annotations.NotThreadSafe; + +@NotThreadSafe // wraps a mutable queue +public class DefaultContinuousAsyncResultSet implements ContinuousAsyncResultSet { + + private final Iterable currentPage; + private final ColumnDefinitions columnDefinitions; + private final int pageNumber; + private final boolean hasMorePages; + private final ExecutionInfo executionInfo; + private final ContinuousCqlRequestHandler handler; + private final CountingIterator iterator; + + public DefaultContinuousAsyncResultSet( + CountingIterator iterator, + ColumnDefinitions columnDefinitions, + int pageNumber, + boolean hasMorePages, + ExecutionInfo executionInfo, + ContinuousCqlRequestHandler handler) { + this.columnDefinitions = columnDefinitions; + this.pageNumber = pageNumber; + this.hasMorePages = hasMorePages; + this.executionInfo = executionInfo; + this.handler = handler; + this.iterator = iterator; + this.currentPage = () -> iterator; + } + + @NonNull + @Override + public ColumnDefinitions getColumnDefinitions() { + return columnDefinitions; + } + + @Override + public boolean wasApplied() { + // always return true for non-conditional updates + return true; + } + + @NonNull + @Override + public ExecutionInfo getExecutionInfo() { + return executionInfo; + } + + @Override + public int pageNumber() { + return pageNumber; + } + + @Override + public boolean hasMorePages() { + return hasMorePages; + } + + @NonNull + @Override + public Iterable currentPage() { + return currentPage; + } + + @Override + public int remaining() { + return iterator.remaining(); + } + + @NonNull + @Override + public CompletionStage fetchNextPage() throws IllegalStateException { + if (!hasMorePages()) { + throw new IllegalStateException( + "Can't call fetchNextPage() on the last page (use hasMorePages() to check)"); + } + return handler.fetchNextPage(); + } + + @Override + public void cancel() { + handler.cancel(); + } + + public static ContinuousAsyncResultSet empty(ExecutionInfo executionInfo) { + + return new ContinuousAsyncResultSet() { + + @NonNull + @Override + public ColumnDefinitions getColumnDefinitions() { + return EmptyColumnDefinitions.INSTANCE; + } + + @NonNull + @Override + public ExecutionInfo getExecutionInfo() { + return executionInfo; + } + + @NonNull + @Override + public Iterable currentPage() { + return Collections.emptyList(); + } + + @Override + public int remaining() { + return 0; + } + + @Override + public boolean hasMorePages() { + return false; + } + + @Override + public int pageNumber() { + return 1; + } + + @NonNull + @Override + public CompletionStage fetchNextPage() + throws IllegalStateException { + throw new IllegalStateException( + "Can't call fetchNextPage() on the last page (use hasMorePages() to check)"); + } + + @Override + public void cancel() { + // noop + } + + @Override + public boolean wasApplied() { + // always true + return true; + } + }; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/cql/continuous/DefaultContinuousResultSet.java b/core/src/main/java/com/datastax/dse/driver/internal/core/cql/continuous/DefaultContinuousResultSet.java new file mode 100644 index 00000000000..929400bc7a6 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/cql/continuous/DefaultContinuousResultSet.java @@ -0,0 +1,132 @@ +/* + * 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 com.datastax.dse.driver.internal.core.cql.continuous; + +import com.datastax.dse.driver.api.core.cql.continuous.ContinuousAsyncResultSet; +import com.datastax.dse.driver.api.core.cql.continuous.ContinuousResultSet; +import com.datastax.oss.driver.api.core.cql.ColumnDefinitions; +import com.datastax.oss.driver.api.core.cql.ExecutionInfo; +import com.datastax.oss.driver.api.core.cql.Row; +import com.datastax.oss.driver.internal.core.util.CountingIterator; +import com.datastax.oss.driver.internal.core.util.concurrent.BlockingOperation; +import com.datastax.oss.driver.internal.core.util.concurrent.CompletableFutures; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import net.jcip.annotations.NotThreadSafe; + +/** + * This class is roughly equivalent to {@link + * com.datastax.oss.driver.internal.core.cql.MultiPageResultSet}, except that {@link + * RowIterator#maybeMoveToNextPage()} needs to check for cancellation before fetching the next page. + */ +@NotThreadSafe +public class DefaultContinuousResultSet implements ContinuousResultSet { + + private final RowIterator iterator; + private final List executionInfos = new ArrayList<>(); + private final ColumnDefinitions columnDefinitions; + + public DefaultContinuousResultSet(ContinuousAsyncResultSet firstPage) { + iterator = new RowIterator(firstPage); + columnDefinitions = firstPage.getColumnDefinitions(); + executionInfos.add(firstPage.getExecutionInfo()); + } + + @Override + public void cancel() { + iterator.cancel(); + } + + @NonNull + @Override + public ColumnDefinitions getColumnDefinitions() { + return columnDefinitions; + } + + @NonNull + @Override + public List getExecutionInfos() { + return executionInfos; + } + + @NonNull + @Override + public Iterator iterator() { + return iterator; + } + + @Override + public boolean isFullyFetched() { + return iterator.isFullyFetched(); + } + + @Override + public int getAvailableWithoutFetching() { + return iterator.remaining(); + } + + @Override + public boolean wasApplied() { + return iterator.wasApplied(); + } + + private class RowIterator extends CountingIterator { + private ContinuousAsyncResultSet currentPage; + private Iterator currentRows; + private boolean cancelled = false; + + private RowIterator(ContinuousAsyncResultSet firstPage) { + super(firstPage.remaining()); + currentPage = firstPage; + currentRows = firstPage.currentPage().iterator(); + } + + @Override + protected Row computeNext() { + maybeMoveToNextPage(); + return currentRows.hasNext() ? currentRows.next() : endOfData(); + } + + private void maybeMoveToNextPage() { + if (!cancelled && !currentRows.hasNext() && currentPage.hasMorePages()) { + BlockingOperation.checkNotDriverThread(); + ContinuousAsyncResultSet nextPage = + CompletableFutures.getUninterruptibly(currentPage.fetchNextPage()); + currentPage = nextPage; + remaining += currentPage.remaining(); + currentRows = nextPage.currentPage().iterator(); + executionInfos.add(nextPage.getExecutionInfo()); + } + } + + private boolean isFullyFetched() { + return !currentPage.hasMorePages(); + } + + private boolean wasApplied() { + return currentPage.wasApplied(); + } + + private void cancel() { + currentPage.cancel(); + cancelled = true; + } + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/cql/continuous/reactive/ContinuousCqlRequestReactiveProcessor.java b/core/src/main/java/com/datastax/dse/driver/internal/core/cql/continuous/reactive/ContinuousCqlRequestReactiveProcessor.java new file mode 100644 index 00000000000..afe0e864181 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/cql/continuous/reactive/ContinuousCqlRequestReactiveProcessor.java @@ -0,0 +1,63 @@ +/* + * 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 com.datastax.dse.driver.internal.core.cql.continuous.reactive; + +import com.datastax.dse.driver.api.core.cql.continuous.reactive.ContinuousReactiveResultSet; +import com.datastax.dse.driver.internal.core.cql.continuous.ContinuousCqlRequestAsyncProcessor; +import com.datastax.dse.driver.internal.core.cql.reactive.FailedReactiveResultSet; +import com.datastax.oss.driver.api.core.cql.Statement; +import com.datastax.oss.driver.api.core.session.Request; +import com.datastax.oss.driver.api.core.type.reflect.GenericType; +import com.datastax.oss.driver.internal.core.context.InternalDriverContext; +import com.datastax.oss.driver.internal.core.session.DefaultSession; +import com.datastax.oss.driver.internal.core.session.RequestProcessor; +import net.jcip.annotations.ThreadSafe; + +@ThreadSafe +public class ContinuousCqlRequestReactiveProcessor + implements RequestProcessor, ContinuousReactiveResultSet> { + + public static final GenericType CONTINUOUS_REACTIVE_RESULT_SET = + GenericType.of(ContinuousReactiveResultSet.class); + + private final ContinuousCqlRequestAsyncProcessor asyncProcessor; + + public ContinuousCqlRequestReactiveProcessor(ContinuousCqlRequestAsyncProcessor asyncProcessor) { + this.asyncProcessor = asyncProcessor; + } + + @Override + public boolean canProcess(Request request, GenericType resultType) { + return request instanceof Statement && resultType.equals(CONTINUOUS_REACTIVE_RESULT_SET); + } + + @Override + public ContinuousReactiveResultSet process( + Statement request, + DefaultSession session, + InternalDriverContext context, + String sessionLogPrefix) { + return new DefaultContinuousReactiveResultSet( + () -> asyncProcessor.process(request, session, context, sessionLogPrefix)); + } + + @Override + public ContinuousReactiveResultSet newFailure(RuntimeException error) { + return new FailedReactiveResultSet(error); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/cql/continuous/reactive/DefaultContinuousReactiveResultSet.java b/core/src/main/java/com/datastax/dse/driver/internal/core/cql/continuous/reactive/DefaultContinuousReactiveResultSet.java new file mode 100644 index 00000000000..b3f301edea6 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/cql/continuous/reactive/DefaultContinuousReactiveResultSet.java @@ -0,0 +1,35 @@ +/* + * 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 com.datastax.dse.driver.internal.core.cql.continuous.reactive; + +import com.datastax.dse.driver.api.core.cql.continuous.ContinuousAsyncResultSet; +import com.datastax.dse.driver.api.core.cql.continuous.reactive.ContinuousReactiveResultSet; +import com.datastax.dse.driver.internal.core.cql.reactive.ReactiveResultSetBase; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletionStage; +import net.jcip.annotations.ThreadSafe; + +@ThreadSafe +public class DefaultContinuousReactiveResultSet + extends ReactiveResultSetBase implements ContinuousReactiveResultSet { + + public DefaultContinuousReactiveResultSet( + Callable> firstPage) { + super(firstPage); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/cql/reactive/CqlRequestReactiveProcessor.java b/core/src/main/java/com/datastax/dse/driver/internal/core/cql/reactive/CqlRequestReactiveProcessor.java new file mode 100644 index 00000000000..3539c2e698c --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/cql/reactive/CqlRequestReactiveProcessor.java @@ -0,0 +1,62 @@ +/* + * 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 com.datastax.dse.driver.internal.core.cql.reactive; + +import com.datastax.dse.driver.api.core.cql.reactive.ReactiveResultSet; +import com.datastax.oss.driver.api.core.cql.Statement; +import com.datastax.oss.driver.api.core.session.Request; +import com.datastax.oss.driver.api.core.type.reflect.GenericType; +import com.datastax.oss.driver.internal.core.context.InternalDriverContext; +import com.datastax.oss.driver.internal.core.cql.CqlRequestAsyncProcessor; +import com.datastax.oss.driver.internal.core.session.DefaultSession; +import com.datastax.oss.driver.internal.core.session.RequestProcessor; +import net.jcip.annotations.ThreadSafe; + +@ThreadSafe +public class CqlRequestReactiveProcessor + implements RequestProcessor, ReactiveResultSet> { + + public static final GenericType REACTIVE_RESULT_SET = + GenericType.of(ReactiveResultSet.class); + + private final CqlRequestAsyncProcessor asyncProcessor; + + public CqlRequestReactiveProcessor(CqlRequestAsyncProcessor asyncProcessor) { + this.asyncProcessor = asyncProcessor; + } + + @Override + public boolean canProcess(Request request, GenericType resultType) { + return request instanceof Statement && resultType.equals(REACTIVE_RESULT_SET); + } + + @Override + public ReactiveResultSet process( + Statement request, + DefaultSession session, + InternalDriverContext context, + String sessionLogPrefix) { + return new DefaultReactiveResultSet( + () -> asyncProcessor.process(request, session, context, sessionLogPrefix)); + } + + @Override + public ReactiveResultSet newFailure(RuntimeException error) { + return new FailedReactiveResultSet(error); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/cql/reactive/DefaultReactiveResultSet.java b/core/src/main/java/com/datastax/dse/driver/internal/core/cql/reactive/DefaultReactiveResultSet.java new file mode 100644 index 00000000000..33b6dc02f48 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/cql/reactive/DefaultReactiveResultSet.java @@ -0,0 +1,31 @@ +/* + * 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 com.datastax.dse.driver.internal.core.cql.reactive; + +import com.datastax.oss.driver.api.core.cql.AsyncResultSet; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletionStage; +import net.jcip.annotations.ThreadSafe; + +@ThreadSafe +public class DefaultReactiveResultSet extends ReactiveResultSetBase { + + public DefaultReactiveResultSet(Callable> firstPage) { + super(firstPage); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/cql/reactive/DefaultReactiveRow.java b/core/src/main/java/com/datastax/dse/driver/internal/core/cql/reactive/DefaultReactiveRow.java new file mode 100644 index 00000000000..ca3b93e7f6b --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/cql/reactive/DefaultReactiveRow.java @@ -0,0 +1,580 @@ +/* + * 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 com.datastax.dse.driver.internal.core.cql.reactive; + +import com.datastax.dse.driver.api.core.cql.reactive.ReactiveRow; +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.api.core.ProtocolVersion; +import com.datastax.oss.driver.api.core.cql.ColumnDefinitions; +import com.datastax.oss.driver.api.core.cql.ExecutionInfo; +import com.datastax.oss.driver.api.core.cql.Row; +import com.datastax.oss.driver.api.core.data.CqlDuration; +import com.datastax.oss.driver.api.core.data.TupleValue; +import com.datastax.oss.driver.api.core.data.UdtValue; +import com.datastax.oss.driver.api.core.detach.AttachmentPoint; +import com.datastax.oss.driver.api.core.metadata.token.Token; +import com.datastax.oss.driver.api.core.type.DataType; +import com.datastax.oss.driver.api.core.type.codec.TypeCodec; +import com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry; +import com.datastax.oss.driver.api.core.type.reflect.GenericType; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.net.InetAddress; +import java.nio.ByteBuffer; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import net.jcip.annotations.NotThreadSafe; + +@NotThreadSafe +class DefaultReactiveRow implements ReactiveRow { + + private final Row row; + private final ExecutionInfo executionInfo; + + DefaultReactiveRow(@NonNull Row row, @NonNull ExecutionInfo executionInfo) { + this.row = row; + this.executionInfo = executionInfo; + } + + @NonNull + @Override + public ExecutionInfo getExecutionInfo() { + return executionInfo; + } + + @NonNull + @Override + public ColumnDefinitions getColumnDefinitions() { + return row.getColumnDefinitions(); + } + + @Override + public ByteBuffer getBytesUnsafe(int i) { + return row.getBytesUnsafe(i); + } + + @Override + public boolean isNull(int i) { + return row.isNull(i); + } + + @Override + public T get(int i, TypeCodec codec) { + return row.get(i, codec); + } + + @Override + public T get(int i, GenericType targetType) { + return row.get(i, targetType); + } + + @Override + public T get(int i, Class targetClass) { + return row.get(i, targetClass); + } + + @Override + public Object getObject(int i) { + return row.getObject(i); + } + + @Override + public boolean getBoolean(int i) { + return row.getBoolean(i); + } + + @Override + public byte getByte(int i) { + return row.getByte(i); + } + + @Override + public double getDouble(int i) { + return row.getDouble(i); + } + + @Override + public float getFloat(int i) { + return row.getFloat(i); + } + + @Override + public int getInt(int i) { + return row.getInt(i); + } + + @Override + public long getLong(int i) { + return row.getLong(i); + } + + @Override + public short getShort(int i) { + return row.getShort(i); + } + + @Override + public Instant getInstant(int i) { + return row.getInstant(i); + } + + @Override + public LocalDate getLocalDate(int i) { + return row.getLocalDate(i); + } + + @Override + public LocalTime getLocalTime(int i) { + return row.getLocalTime(i); + } + + @Override + public ByteBuffer getByteBuffer(int i) { + return row.getByteBuffer(i); + } + + @Override + public String getString(int i) { + return row.getString(i); + } + + @Override + public BigInteger getBigInteger(int i) { + return row.getBigInteger(i); + } + + @Override + public BigDecimal getBigDecimal(int i) { + return row.getBigDecimal(i); + } + + @Override + public UUID getUuid(int i) { + return row.getUuid(i); + } + + @Override + public InetAddress getInetAddress(int i) { + return row.getInetAddress(i); + } + + @Override + public CqlDuration getCqlDuration(int i) { + return row.getCqlDuration(i); + } + + @Override + public Token getToken(int i) { + return row.getToken(i); + } + + @Override + public List getList(int i, @NonNull Class elementsClass) { + return row.getList(i, elementsClass); + } + + @Override + public Set getSet(int i, @NonNull Class elementsClass) { + return row.getSet(i, elementsClass); + } + + @Override + public Map getMap(int i, @NonNull Class keyClass, @NonNull Class valueClass) { + return row.getMap(i, keyClass, valueClass); + } + + @Override + public UdtValue getUdtValue(int i) { + return row.getUdtValue(i); + } + + @Override + public TupleValue getTupleValue(int i) { + return row.getTupleValue(i); + } + + @Override + public int size() { + return row.size(); + } + + @NonNull + @Override + public DataType getType(int i) { + return row.getType(i); + } + + @NonNull + @Override + public CodecRegistry codecRegistry() { + return row.codecRegistry(); + } + + @NonNull + @Override + public ProtocolVersion protocolVersion() { + return row.protocolVersion(); + } + + @Override + public ByteBuffer getBytesUnsafe(@NonNull String name) { + return row.getBytesUnsafe(name); + } + + @Override + public boolean isNull(@NonNull String name) { + return row.isNull(name); + } + + @Override + public T get(@NonNull String name, @NonNull TypeCodec codec) { + return row.get(name, codec); + } + + @Override + public T get(@NonNull String name, @NonNull GenericType targetType) { + return row.get(name, targetType); + } + + @Override + public T get(@NonNull String name, @NonNull Class targetClass) { + return row.get(name, targetClass); + } + + @Override + public Object getObject(@NonNull String name) { + return row.getObject(name); + } + + @Override + public boolean getBoolean(@NonNull String name) { + return row.getBoolean(name); + } + + @Override + public byte getByte(@NonNull String name) { + return row.getByte(name); + } + + @Override + public double getDouble(@NonNull String name) { + return row.getDouble(name); + } + + @Override + public float getFloat(@NonNull String name) { + return row.getFloat(name); + } + + @Override + public int getInt(@NonNull String name) { + return row.getInt(name); + } + + @Override + public long getLong(@NonNull String name) { + return row.getLong(name); + } + + @Override + public short getShort(@NonNull String name) { + return row.getShort(name); + } + + @Override + public Instant getInstant(@NonNull String name) { + return row.getInstant(name); + } + + @Override + public LocalDate getLocalDate(@NonNull String name) { + return row.getLocalDate(name); + } + + @Override + public LocalTime getLocalTime(@NonNull String name) { + return row.getLocalTime(name); + } + + @Override + public ByteBuffer getByteBuffer(@NonNull String name) { + return row.getByteBuffer(name); + } + + @Override + public String getString(@NonNull String name) { + return row.getString(name); + } + + @Override + public BigInteger getBigInteger(@NonNull String name) { + return row.getBigInteger(name); + } + + @Override + public BigDecimal getBigDecimal(@NonNull String name) { + return row.getBigDecimal(name); + } + + @Override + public UUID getUuid(@NonNull String name) { + return row.getUuid(name); + } + + @Override + public InetAddress getInetAddress(@NonNull String name) { + return row.getInetAddress(name); + } + + @Override + public CqlDuration getCqlDuration(@NonNull String name) { + return row.getCqlDuration(name); + } + + @Override + public Token getToken(@NonNull String name) { + return row.getToken(name); + } + + @Override + public List getList(@NonNull String name, @NonNull Class elementsClass) { + return row.getList(name, elementsClass); + } + + @Override + public Set getSet(@NonNull String name, @NonNull Class elementsClass) { + return row.getSet(name, elementsClass); + } + + @Override + public Map getMap( + @NonNull String name, @NonNull Class keyClass, @NonNull Class valueClass) { + return row.getMap(name, keyClass, valueClass); + } + + @Override + public UdtValue getUdtValue(@NonNull String name) { + return row.getUdtValue(name); + } + + @Override + public TupleValue getTupleValue(@NonNull String name) { + return row.getTupleValue(name); + } + + @NonNull + @Override + public List allIndicesOf(@NonNull String name) { + return row.allIndicesOf(name); + } + + @Override + public int firstIndexOf(@NonNull String name) { + return row.firstIndexOf(name); + } + + @NonNull + @Override + public DataType getType(@NonNull String name) { + return row.getType(name); + } + + @Override + public ByteBuffer getBytesUnsafe(@NonNull CqlIdentifier id) { + return row.getBytesUnsafe(id); + } + + @Override + public boolean isNull(@NonNull CqlIdentifier id) { + return row.isNull(id); + } + + @Override + public T get(@NonNull CqlIdentifier id, @NonNull TypeCodec codec) { + return row.get(id, codec); + } + + @Override + public T get(@NonNull CqlIdentifier id, @NonNull GenericType targetType) { + return row.get(id, targetType); + } + + @Override + public T get(@NonNull CqlIdentifier id, @NonNull Class targetClass) { + return row.get(id, targetClass); + } + + @Override + public Object getObject(@NonNull CqlIdentifier id) { + return row.getObject(id); + } + + @Override + public boolean getBoolean(@NonNull CqlIdentifier id) { + return row.getBoolean(id); + } + + @Override + public byte getByte(@NonNull CqlIdentifier id) { + return row.getByte(id); + } + + @Override + public double getDouble(@NonNull CqlIdentifier id) { + return row.getDouble(id); + } + + @Override + public float getFloat(@NonNull CqlIdentifier id) { + return row.getFloat(id); + } + + @Override + public int getInt(@NonNull CqlIdentifier id) { + return row.getInt(id); + } + + @Override + public long getLong(@NonNull CqlIdentifier id) { + return row.getLong(id); + } + + @Override + public short getShort(@NonNull CqlIdentifier id) { + return row.getShort(id); + } + + @Override + public Instant getInstant(@NonNull CqlIdentifier id) { + return row.getInstant(id); + } + + @Override + public LocalDate getLocalDate(@NonNull CqlIdentifier id) { + return row.getLocalDate(id); + } + + @Override + public LocalTime getLocalTime(@NonNull CqlIdentifier id) { + return row.getLocalTime(id); + } + + @Override + public ByteBuffer getByteBuffer(@NonNull CqlIdentifier id) { + return row.getByteBuffer(id); + } + + @Override + public String getString(@NonNull CqlIdentifier id) { + return row.getString(id); + } + + @Override + public BigInteger getBigInteger(@NonNull CqlIdentifier id) { + return row.getBigInteger(id); + } + + @Override + public BigDecimal getBigDecimal(@NonNull CqlIdentifier id) { + return row.getBigDecimal(id); + } + + @Override + public UUID getUuid(@NonNull CqlIdentifier id) { + return row.getUuid(id); + } + + @Override + public InetAddress getInetAddress(@NonNull CqlIdentifier id) { + return row.getInetAddress(id); + } + + @Override + public CqlDuration getCqlDuration(@NonNull CqlIdentifier id) { + return row.getCqlDuration(id); + } + + @Override + public Token getToken(@NonNull CqlIdentifier id) { + return row.getToken(id); + } + + @Override + public List getList(@NonNull CqlIdentifier id, @NonNull Class elementsClass) { + return row.getList(id, elementsClass); + } + + @Override + public Set getSet(@NonNull CqlIdentifier id, @NonNull Class elementsClass) { + return row.getSet(id, elementsClass); + } + + @Override + public Map getMap( + @NonNull CqlIdentifier id, @NonNull Class keyClass, @NonNull Class valueClass) { + return row.getMap(id, keyClass, valueClass); + } + + @Override + public UdtValue getUdtValue(@NonNull CqlIdentifier id) { + return row.getUdtValue(id); + } + + @Override + public TupleValue getTupleValue(@NonNull CqlIdentifier id) { + return row.getTupleValue(id); + } + + @NonNull + @Override + public List allIndicesOf(@NonNull CqlIdentifier id) { + return row.allIndicesOf(id); + } + + @Override + public int firstIndexOf(@NonNull CqlIdentifier id) { + return row.firstIndexOf(id); + } + + @NonNull + @Override + public DataType getType(@NonNull CqlIdentifier id) { + return row.getType(id); + } + + @Override + public boolean isDetached() { + return row.isDetached(); + } + + @Override + public void attach(@NonNull AttachmentPoint attachmentPoint) { + row.attach(attachmentPoint); + } + + @Override + public String toString() { + return "DefaultReactiveRow{row=" + row + ", executionInfo=" + executionInfo + '}'; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/cql/reactive/EmptySubscription.java b/core/src/main/java/com/datastax/dse/driver/internal/core/cql/reactive/EmptySubscription.java new file mode 100644 index 00000000000..f760ecc395e --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/cql/reactive/EmptySubscription.java @@ -0,0 +1,33 @@ +/* + * 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 com.datastax.dse.driver.internal.core.cql.reactive; + +import org.reactivestreams.Subscription; + +public class EmptySubscription implements Subscription { + + public static final EmptySubscription INSTANCE = new EmptySubscription(); + + private EmptySubscription() {} + + @Override + public void request(long n) {} + + @Override + public void cancel() {} +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/cql/reactive/FailedPublisher.java b/core/src/main/java/com/datastax/dse/driver/internal/core/cql/reactive/FailedPublisher.java new file mode 100644 index 00000000000..638434bb2d0 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/cql/reactive/FailedPublisher.java @@ -0,0 +1,49 @@ +/* + * 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 com.datastax.dse.driver.internal.core.cql.reactive; + +import java.util.Objects; +import org.reactivestreams.Publisher; +import org.reactivestreams.Subscriber; + +/** + * A {@link Publisher} that immediately signals the error passed at instantiation to all its + * subscribers. + */ +public class FailedPublisher implements Publisher { + + protected final Throwable error; + + public FailedPublisher(Throwable error) { + this.error = error; + } + + @Override + public void subscribe(Subscriber subscriber) { + Objects.requireNonNull(subscriber, "Subscriber cannot be null"); + // Per rule 1.9, we need to call onSubscribe before any other signal. Pass a dummy + // subscription since we know it will never be used. + subscriber.onSubscribe(EmptySubscription.INSTANCE); + // Signal the error to the subscriber right away. This is safe to do because per rule 2.10, + // a Subscriber MUST be prepared to receive an onError signal without a preceding + // Subscription.request(long n) call. + // Also, per rule 2.13: onError MUST return normally except when any provided parameter + // is null (which is not the case here); so we don't need care about catching errors here. + subscriber.onError(error); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/cql/reactive/FailedReactiveResultSet.java b/core/src/main/java/com/datastax/dse/driver/internal/core/cql/reactive/FailedReactiveResultSet.java new file mode 100644 index 00000000000..31c34d649aa --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/cql/reactive/FailedReactiveResultSet.java @@ -0,0 +1,60 @@ +/* + * 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 com.datastax.dse.driver.internal.core.cql.reactive; + +import com.datastax.dse.driver.api.core.cql.continuous.reactive.ContinuousReactiveResultSet; +import com.datastax.dse.driver.api.core.cql.reactive.ReactiveResultSet; +import com.datastax.dse.driver.api.core.cql.reactive.ReactiveRow; +import com.datastax.dse.driver.internal.core.cql.continuous.reactive.ContinuousCqlRequestReactiveProcessor; +import com.datastax.oss.driver.api.core.cql.ColumnDefinitions; +import com.datastax.oss.driver.api.core.cql.ExecutionInfo; +import edu.umd.cs.findbugs.annotations.NonNull; +import org.reactivestreams.Publisher; + +/** + * A {@link ReactiveResultSet} that immediately signals the error passed at instantiation to all its + * subscribers. + * + * @see CqlRequestReactiveProcessor#newFailure(java.lang.RuntimeException) + * @see ContinuousCqlRequestReactiveProcessor#newFailure(java.lang.RuntimeException) + */ +public class FailedReactiveResultSet extends FailedPublisher + implements ReactiveResultSet, ContinuousReactiveResultSet { + + public FailedReactiveResultSet(Throwable error) { + super(error); + } + + @NonNull + @Override + public Publisher getColumnDefinitions() { + return new FailedPublisher<>(error); + } + + @NonNull + @Override + public Publisher getExecutionInfos() { + return new FailedPublisher<>(error); + } + + @NonNull + @Override + public Publisher wasApplied() { + return new FailedPublisher<>(error); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/cql/reactive/ReactiveOperators.java b/core/src/main/java/com/datastax/dse/driver/internal/core/cql/reactive/ReactiveOperators.java new file mode 100644 index 00000000000..f058149f570 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/cql/reactive/ReactiveOperators.java @@ -0,0 +1,61 @@ +/* + * 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 com.datastax.dse.driver.internal.core.cql.reactive; + +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.concurrent.atomic.AtomicLong; + +public final class ReactiveOperators { + + /** + * Atomically adds the given value to the given AtomicLong, bound to Long.MAX_VALUE. + * + * @param current the current value. + * @param toAdd the delta to add. + */ + public static void addCap(@NonNull AtomicLong current, long toAdd) { + long r, u; + do { + r = current.get(); + if (r == Long.MAX_VALUE) { + return; + } + u = r + toAdd; + if (u < 0L) { + u = Long.MAX_VALUE; + } + } while (!current.compareAndSet(r, u)); + } + + /** + * Atomically subtracts the given value from the given AtomicLong, bound to 0. + * + * @param current the current value. + * @param toSub the delta to subtract. + */ + public static void subCap(@NonNull AtomicLong current, long toSub) { + long r, u; + do { + r = current.get(); + if (r == 0 || r == Long.MAX_VALUE) { + return; + } + u = Math.max(r - toSub, 0); + } while (!current.compareAndSet(r, u)); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/cql/reactive/ReactiveResultSetBase.java b/core/src/main/java/com/datastax/dse/driver/internal/core/cql/reactive/ReactiveResultSetBase.java new file mode 100644 index 00000000000..5ba00e22298 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/cql/reactive/ReactiveResultSetBase.java @@ -0,0 +1,105 @@ +/* + * 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 com.datastax.dse.driver.internal.core.cql.reactive; + +import com.datastax.dse.driver.api.core.cql.reactive.ReactiveResultSet; +import com.datastax.dse.driver.api.core.cql.reactive.ReactiveRow; +import com.datastax.oss.driver.api.core.AsyncPagingIterable; +import com.datastax.oss.driver.api.core.cql.ColumnDefinitions; +import com.datastax.oss.driver.api.core.cql.ExecutionInfo; +import com.datastax.oss.driver.api.core.cql.Row; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.Objects; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.atomic.AtomicBoolean; +import net.jcip.annotations.ThreadSafe; +import org.reactivestreams.Publisher; +import org.reactivestreams.Subscriber; + +@ThreadSafe +public abstract class ReactiveResultSetBase> + implements ReactiveResultSet { + + private final Callable> firstPage; + + private final AtomicBoolean alreadySubscribed = new AtomicBoolean(false); + + private final SimpleUnicastProcessor columnDefinitionsPublisher = + new SimpleUnicastProcessor<>(); + + private final SimpleUnicastProcessor executionInfosPublisher = + new SimpleUnicastProcessor<>(); + + private final SimpleUnicastProcessor wasAppliedPublisher = + new SimpleUnicastProcessor<>(); + + protected ReactiveResultSetBase(Callable> firstPage) { + this.firstPage = firstPage; + } + + @Override + public void subscribe(@NonNull Subscriber subscriber) { + // As per rule 1.9, we need to throw an NPE if subscriber is null + Objects.requireNonNull(subscriber, "Subscriber cannot be null"); + // As per rule 1.11, this publisher is allowed to support only one subscriber. + if (alreadySubscribed.compareAndSet(false, true)) { + ReactiveResultSetSubscription subscription = + new ReactiveResultSetSubscription<>( + subscriber, columnDefinitionsPublisher, executionInfosPublisher, wasAppliedPublisher); + try { + subscriber.onSubscribe(subscription); + // must be done after onSubscribe + subscription.start(firstPage); + } catch (Throwable t) { + // As per rule 2.13: In the case that this rule is violated, + // any associated Subscription to the Subscriber MUST be considered as + // cancelled, and the caller MUST raise this error condition in a fashion + // that is adequate for the runtime environment. + subscription.doOnError( + new IllegalStateException( + subscriber + + " violated the Reactive Streams rule 2.13 by throwing an exception from onSubscribe.", + t)); + } + } else { + subscriber.onSubscribe(EmptySubscription.INSTANCE); + subscriber.onError( + new IllegalStateException("This publisher does not support multiple subscriptions")); + } + // As per 2.13, this method must return normally (i.e. not throw) + } + + @NonNull + @Override + public Publisher getColumnDefinitions() { + return columnDefinitionsPublisher; + } + + @NonNull + @Override + public Publisher getExecutionInfos() { + return executionInfosPublisher; + } + + @NonNull + @Override + public Publisher wasApplied() { + return wasAppliedPublisher; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/cql/reactive/ReactiveResultSetSubscription.java b/core/src/main/java/com/datastax/dse/driver/internal/core/cql/reactive/ReactiveResultSetSubscription.java new file mode 100644 index 00000000000..500a291e9d2 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/cql/reactive/ReactiveResultSetSubscription.java @@ -0,0 +1,493 @@ +/* + * 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 com.datastax.dse.driver.internal.core.cql.reactive; + +import com.datastax.dse.driver.api.core.cql.reactive.ReactiveRow; +import com.datastax.dse.driver.internal.core.util.concurrent.BoundedConcurrentQueue; +import com.datastax.oss.driver.api.core.AsyncPagingIterable; +import com.datastax.oss.driver.api.core.cql.ColumnDefinitions; +import com.datastax.oss.driver.api.core.cql.ExecutionInfo; +import com.datastax.oss.driver.api.core.cql.Row; +import com.datastax.oss.driver.internal.core.util.concurrent.CompletableFutures; +import com.datastax.oss.driver.shaded.guava.common.collect.Iterators; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.util.Collections; +import java.util.Iterator; +import java.util.Objects; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import net.jcip.annotations.ThreadSafe; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A single-subscriber subscription that executes one single query and emits all the returned rows. + * + *

This class can handle both continuous and non-continuous result sets. + */ +@ThreadSafe +public class ReactiveResultSetSubscription> + implements Subscription { + + private static final Logger LOG = LoggerFactory.getLogger(ReactiveResultSetSubscription.class); + + private static final int MAX_ENQUEUED_PAGES = 4; + + /** Tracks the number of items requested by the subscriber. */ + private final AtomicLong requested = new AtomicLong(0); + + /** The pages received so far, with a maximum of MAX_ENQUEUED_PAGES elements. */ + private final BoundedConcurrentQueue> pages = + new BoundedConcurrentQueue<>(MAX_ENQUEUED_PAGES); + + /** + * Used to signal that a thread is currently draining, i.e., emitting items to the subscriber. + * When it is zero, that means there is no ongoing emission. This mechanism effectively serializes + * access to the drain() method, and also keeps track of missed attempts to enter it, since each + * thread that attempts to drain will increment this counter. + * + * @see #drain() + */ + private final AtomicInteger draining = new AtomicInteger(0); + + /** + * Waited upon by the driver and completed when the subscriber requests its first item. + * + *

Used to hold off emitting results until the subscriber issues its first request for items. + * Since this future is only completed from {@link #request(long)}, this effectively conditions + * the enqueueing of the first page to the reception of the subscriber's first request. + * + *

This mechanism avoids sending terminal signals before a request is made when the stream is + * empty. Note that as per 2.9, "a Subscriber MUST be prepared to receive an onComplete signal + * with or without a preceding Subscription.request(long n) call." However, the TCK considers it + * as unfair behavior. + * + * @see #start(Callable) + * @see #request(long) + */ + private final CompletableFuture firstSubscriberRequestArrived = new CompletableFuture<>(); + + /** non-final because it has to be de-referenced, see {@link #clear()}. */ + private volatile Subscriber mainSubscriber; + + private volatile Subscriber columnDefinitionsSubscriber; + + private volatile Subscriber executionInfosSubscriber; + + private volatile Subscriber wasAppliedSubscriber; + + /** + * Set to true when the subscription is cancelled, which happens when an error is encountered, + * when the result set is fully consumed and the subscription terminates, or when the subscriber + * manually calls {@link #cancel()}. + */ + private volatile boolean cancelled = false; + + ReactiveResultSetSubscription( + @NonNull Subscriber mainSubscriber, + @NonNull Subscriber columnDefinitionsSubscriber, + @NonNull Subscriber executionInfosSubscriber, + @NonNull Subscriber wasAppliedSubscriber) { + this.mainSubscriber = mainSubscriber; + this.columnDefinitionsSubscriber = columnDefinitionsSubscriber; + this.executionInfosSubscriber = executionInfosSubscriber; + this.wasAppliedSubscriber = wasAppliedSubscriber; + } + + /** + * Starts the query execution. + * + *

Must be called immediately after creating the subscription, but after {@link + * Subscriber#onSubscribe(Subscription)}. + * + * @param firstPage The future that, when complete, will produce the first page. + */ + void start(@NonNull Callable> firstPage) { + firstSubscriberRequestArrived.thenAccept( + (aVoid) -> fetchNextPageAndEnqueue(new Page<>(firstPage), true)); + } + + @Override + public void request(long n) { + // As per 3.6: after the Subscription is cancelled, additional + // calls to request() MUST be NOPs. + if (!cancelled) { + if (n < 1) { + // Validate request as per rule 3.9 + doOnError( + new IllegalArgumentException( + mainSubscriber + + " violated the Reactive Streams rule 3.9 by requesting a non-positive number of elements.")); + } else { + // As per rule 3.17, when demand overflows Long.MAX_VALUE + // it can be treated as "effectively unbounded" + ReactiveOperators.addCap(requested, n); + // Set the first future to true if not done yet. + // This will make the first page of results ready for consumption, + // see start(). + // As per 2.7 it is the subscriber's responsibility to provide + // external synchronization when calling request(), + // so the check-then-act idiom below is good enough + // (and besides, complete() is idempotent). + if (!firstSubscriberRequestArrived.isDone()) { + firstSubscriberRequestArrived.complete(null); + } + drain(); + } + } + } + + @Override + public void cancel() { + // As per 3.5: Subscription.cancel() MUST respect the responsiveness of + // its caller by returning in a timely manner, MUST be idempotent and + // MUST be thread-safe. + if (!cancelled) { + cancelled = true; + if (draining.getAndIncrement() == 0) { + // If nobody is draining, clear now; + // otherwise, the draining thread will notice + // that the cancelled flag was set + // and will clear for us. + clear(); + } + } + } + + /** + * Attempts to drain available items, i.e. emit them to the subscriber. + * + *

Access to this method is serialized by the field {@link #draining}: only one thread at a + * time can drain, but threads that attempt to drain while other thread is already draining + * increment that field; the draining thread, before finishing its work, checks for such failed + * attempts and triggers another round of draining if that was the case. + * + *

The loop is interrupted when 1) the requested amount has been met or 2) when there are no + * more items readily available or 3) the subscription has been cancelled. + * + *

The loop also checks for stream exhaustion and emits a terminal {@code onComplete} signal in + * this case. + * + *

This method may run on a driver IO thread when invoked from {@link + * #fetchNextPageAndEnqueue(Page, boolean)}, or on a subscriber thread, when invoked from {@link + * #request(long)}. + */ + @SuppressWarnings("ConditionalBreakInInfiniteLoop") + private void drain() { + // As per 3.4: this method SHOULD respect the responsiveness + // of its caller by returning in a timely manner. + // We accomplish this by a wait-free implementation. + if (draining.getAndIncrement() != 0) { + // Someone else is already draining, so do nothing, + // the other thread will notice that we attempted to drain. + // This also allows to abide by rule 3.3 and avoid + // cycles such as request() -> onNext() -> request() etc. + return; + } + int missed = 1; + // Note: when termination is detected inside this loop, + // we MUST call clear() manually. + for (; ; ) { + // The requested number of items at this point + long r = requested.get(); + // The number of items emitted thus far + long emitted = 0L; + while (emitted != r) { + if (cancelled) { + clear(); + return; + } + Object result; + try { + result = tryNext(); + } catch (Throwable t) { + doOnError(t); + clear(); + return; + } + if (result == null) { + break; + } + if (result instanceof Throwable) { + doOnError((Throwable) result); + clear(); + return; + } + doOnNext((ReactiveRow) result); + emitted++; + } + if (isExhausted()) { + doOnComplete(); + clear(); + return; + } + if (cancelled) { + clear(); + return; + } + if (emitted != 0) { + // if any item was emitted, adjust the requested field + ReactiveOperators.subCap(requested, emitted); + } + // if another thread tried to call drain() while we were busy, + // then we should do another drain round. + missed = draining.addAndGet(-missed); + if (missed == 0) { + break; + } + } + } + + /** + * Tries to return the next item, if one is readily available, and returns {@code null} otherwise. + * + *

Cannot run concurrently due to the {@link #draining} field. + */ + @Nullable + private Object tryNext() { + Page current = pages.peek(); + if (current != null) { + if (current.hasMoreRows()) { + return current.nextRow(); + } else if (current.hasMorePages()) { + // Discard current page as it is consumed. + // Don't discard the last page though as we need it + // to test isExhausted(). It will be GC'ed when a terminal signal + // is issued anyway, so that's no big deal. + if (pages.poll() == null) { + throw new AssertionError("Queue is empty, this should not happen"); + } + // if the next page is readily available, + // serve its first row now, no need to wait + // for the next drain. + return tryNext(); + } + } + // No items available right now. + return null; + } + + /** + * Returns {@code true} when the entire stream has been consumed and no more items can be emitted. + * When that is the case, a terminal signal is sent. + * + *

Cannot run concurrently due to the draining field. + */ + private boolean isExhausted() { + Page current = pages.peek(); + // Note: current can only be null when: + // 1) we are waiting for the first page and it hasn't arrived yet; + // 2) we just discarded the current page, but the next page hasn't arrived yet. + // In any case, a null here means it is not the last page, since the last page + // stays in the queue until the very end of the operation. + return current != null && !current.hasMoreRows() && !current.hasMorePages(); + } + + /** + * Runs on a subscriber thread initially, see {@link #start(Callable)}. Subsequent executions run + * on the thread that completes the pair of futures [current.fetchNextPage, pages.offer] and + * enqueues. This can be a driver IO thread or a subscriber thread; in both cases, cannot run + * concurrently due to the fact that one can only fetch the next page when the current one is + * arrived and enqueued. + */ + private void fetchNextPageAndEnqueue(@NonNull Page current, boolean firstPage) { + current + .fetchNextPage() + // as soon as the response arrives, + // create the new page + .handle( + (rs, t) -> { + Page page; + if (t == null) { + page = toPage(rs); + executionInfosSubscriber.onNext(rs.getExecutionInfo()); + if (!page.hasMorePages()) { + executionInfosSubscriber.onComplete(); + } + if (firstPage) { + columnDefinitionsSubscriber.onNext(rs.getColumnDefinitions()); + columnDefinitionsSubscriber.onComplete(); + // Avoid calling wasApplied on empty pages as some implementations may throw + // IllegalStateException; if the page is empty, this wasn't a CAS query, in which + // case, as per the method's contract, wasApplied should be true. + boolean wasApplied = rs.remaining() == 0 || rs.wasApplied(); + wasAppliedSubscriber.onNext(wasApplied); + wasAppliedSubscriber.onComplete(); + } + } else { + // Unwrap CompletionExceptions created by combined futures + if (t instanceof CompletionException) { + t = t.getCause(); + } + page = toErrorPage(t); + executionInfosSubscriber.onError(t); + if (firstPage) { + columnDefinitionsSubscriber.onError(t); + wasAppliedSubscriber.onError(t); + } + } + return page; + }) + .thenCompose(pages::offer) + .thenAccept( + page -> { + if (page.hasMorePages() && !cancelled) { + // preemptively fetch the next page, if available + fetchNextPageAndEnqueue(page, false); + } + drain(); + }); + } + + private void doOnNext(@NonNull ReactiveRow result) { + try { + mainSubscriber.onNext(result); + } catch (Throwable t) { + LOG.error( + mainSubscriber + + " violated the Reactive Streams rule 2.13 by throwing an exception from onNext.", + t); + cancel(); + } + } + + private void doOnComplete() { + try { + // Then we signal onComplete as per rules 1.2 and 1.5 + mainSubscriber.onComplete(); + } catch (Throwable t) { + LOG.error( + mainSubscriber + + " violated the Reactive Streams rule 2.13 by throwing an exception from onComplete.", + t); + } + // We need to consider this Subscription as cancelled as per rule 1.6 + cancel(); + } + + // package-private because it can be invoked by the publisher if the subscription handshake + // process fails. + void doOnError(@NonNull Throwable error) { + try { + // Then we signal the error downstream, as per rules 1.2 and 1.4. + mainSubscriber.onError(error); + } catch (Throwable t) { + t.addSuppressed(error); + LOG.error( + mainSubscriber + + " violated the Reactive Streams rule 2.13 by throwing an exception from onError.", + t); + } + // We need to consider this Subscription as cancelled as per rule 1.6 + cancel(); + } + + private void clear() { + // We don't need these pages anymore and should not hold references + // to them. + pages.clear(); + // As per 3.13, Subscription.cancel() MUST request the Publisher to + // eventually drop any references to the corresponding subscriber. + // Our own publishers do not keep references to this subscription, + // but downstream processors might do so, which is why we need to + // defensively clear the subscriber reference when we are done. + mainSubscriber = null; + columnDefinitionsSubscriber = null; + executionInfosSubscriber = null; + wasAppliedSubscriber = null; + } + + /** + * Converts the received result object into a {@link Page}. + * + * @param rs the result object to convert. + * @return a new page. + */ + @NonNull + private Page toPage(@NonNull ResultSetT rs) { + ExecutionInfo executionInfo = rs.getExecutionInfo(); + Iterator results = + Iterators.transform( + rs.currentPage().iterator(), + row -> new DefaultReactiveRow(Objects.requireNonNull(row), executionInfo)); + return new Page<>(results, rs.hasMorePages() ? rs::fetchNextPage : null); + } + + /** Converts the given error into a {@link Page}, containing the error as its only element. */ + @NonNull + private Page toErrorPage(@NonNull Throwable t) { + return new Page<>(Iterators.singletonIterator(t), null); + } + + /** + * A page object comprises an iterator over the page's results, and a future pointing to the next + * page (or {@code null}, if it's the last page). + */ + static class Page> { + + @NonNull final Iterator iterator; + + // A pointer to the next page, or null if this is the last page. + @Nullable final Callable> nextPage; + + /** called only from start() */ + Page(@NonNull Callable> nextPage) { + this.iterator = Collections.emptyIterator(); + this.nextPage = nextPage; + } + + Page(@NonNull Iterator iterator, @Nullable Callable> nextPage) { + this.iterator = iterator; + this.nextPage = nextPage; + } + + boolean hasMorePages() { + return nextPage != null; + } + + @NonNull + CompletionStage fetchNextPage() { + try { + return Objects.requireNonNull(nextPage).call(); + } catch (Exception e) { + // This is a synchronous failure in the driver. + // It can happen in rare cases when the driver throws an exception instead of returning a + // failed future; e.g. if someone tries to execute a continuous paging request but the + // protocol version in use does not support it. + // We treat it as a failed future. + return CompletableFutures.failedFuture(e); + } + } + + boolean hasMoreRows() { + return iterator.hasNext(); + } + + @NonNull + Object nextRow() { + return iterator.next(); + } + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/cql/reactive/SimpleUnicastProcessor.java b/core/src/main/java/com/datastax/dse/driver/internal/core/cql/reactive/SimpleUnicastProcessor.java new file mode 100644 index 00000000000..845cbe2349b --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/cql/reactive/SimpleUnicastProcessor.java @@ -0,0 +1,265 @@ +/* + * 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 com.datastax.dse.driver.internal.core.cql.reactive; + +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.Objects; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedDeque; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import org.reactivestreams.Processor; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A simple {@link Processor} that receives items form an upstream publisher, stores them in an + * internal queue, then serves them to one single downstream subscriber. It does not support + * multiple subscriptions. + * + *

Implementation note: this class is intended to serve as the common implementation for all + * secondary publishers exposed by the driver's reactive API, and in particular, for publishers of + * query metadata objects. Since such publishers are not critical, and usually only publish a + * handful of items, this implementation favors simplicity over efficiency (in particular, it uses + * an unbounded linked queue, but in practice there is no risk that this queue could grow + * uncontrollably). + * + * @param The type of elements received and emitted by this processor. + */ +public class SimpleUnicastProcessor + implements Processor, Subscription { + + private static final Logger LOG = LoggerFactory.getLogger(SimpleUnicastProcessor.class); + + private static final Object ON_COMPLETE = new Object(); + + private final Queue queue = new ConcurrentLinkedDeque<>(); + + private final AtomicBoolean once = new AtomicBoolean(false); + + private final AtomicInteger draining = new AtomicInteger(0); + + private final AtomicLong requested = new AtomicLong(0); + + private volatile Subscriber subscriber; + + private volatile boolean cancelled; + + @Override + public void subscribe(Subscriber subscriber) { + // As per rule 1.9, we need to throw an NPE if subscriber is null + Objects.requireNonNull(subscriber, "Subscriber cannot be null"); + // As per rule 1.11, this publisher supports only one subscriber. + if (once.compareAndSet(false, true)) { + this.subscriber = subscriber; + try { + subscriber.onSubscribe(this); + } catch (Throwable t) { + // As per rule 2.13: In the case that this rule is violated, + // any associated Subscription to the Subscriber MUST be considered as + // cancelled, and the caller MUST raise this error condition in a fashion + // that is adequate for the runtime environment. + doOnError( + new IllegalStateException( + subscriber + + " violated the Reactive Streams rule 2.13 by throwing an exception from onSubscribe.", + t)); + } + } else { + subscriber.onSubscribe(EmptySubscription.INSTANCE); + subscriber.onError( + new IllegalStateException("This publisher does not support multiple subscriptions")); + } + // As per 2.13, this method must return normally (i.e. not throw) + } + + @Override + public void onSubscribe(Subscription s) { + // no-op + } + + @Override + public void onNext(ElementT value) { + if (!cancelled) { + queue.offer(value); + drain(); + } + } + + @Override + public void onError(Throwable error) { + if (!cancelled) { + queue.offer(error); + drain(); + } + } + + @Override + public void onComplete() { + if (!cancelled) { + queue.offer(ON_COMPLETE); + drain(); + } + } + + @Override + public void request(long n) { + // As per 3.6: after the Subscription is cancelled, additional + // calls to request() MUST be NOPs. + if (!cancelled) { + if (n < 1) { + // Validate request as per rule 3.9 + doOnError( + new IllegalArgumentException( + subscriber + + " violated the Reactive Streams rule 3.9 by requesting a non-positive number of elements.")); + } else { + // As per rule 3.17, when demand overflows Long.MAX_VALUE + // it can be treated as "effectively unbounded" + ReactiveOperators.addCap(requested, n); + drain(); + } + } + } + + @Override + public void cancel() { + // As per 3.5: Subscription.cancel() MUST respect the responsiveness of + // its caller by returning in a timely manner, MUST be idempotent and + // MUST be thread-safe. + if (!cancelled) { + cancelled = true; + if (draining.getAndIncrement() == 0) { + // If nobody is draining, clear now; + // otherwise, the draining thread will notice + // that the cancelled flag was set + // and will clear for us. + clear(); + } + } + } + + @SuppressWarnings("ConditionalBreakInInfiniteLoop") + private void drain() { + if (draining.getAndIncrement() != 0) { + return; + } + int missed = 1; + for (; ; ) { + // Note: when termination is detected inside this loop, + // we MUST call clear() manually. + long requested = this.requested.get(); + long emitted = 0L; + while (requested != emitted) { + if (cancelled) { + clear(); + return; + } + Object t = queue.poll(); + if (t == null) { + break; + } + if (t instanceof Throwable) { + Throwable error = (Throwable) t; + doOnError(error); + clear(); + return; + } else if (t == ON_COMPLETE) { + doOnComplete(); + clear(); + return; + } else { + @SuppressWarnings("unchecked") + ElementT item = (ElementT) t; + doOnNext(item); + emitted++; + } + } + if (cancelled) { + clear(); + return; + } + if (emitted != 0) { + // if any item was emitted, adjust the requested field + ReactiveOperators.subCap(this.requested, emitted); + } + // if another thread tried to call drain() while we were busy, + // then we should do another drain round. + missed = draining.addAndGet(-missed); + if (missed == 0) { + break; + } + } + } + + private void doOnNext(@NonNull ElementT result) { + try { + subscriber.onNext(result); + } catch (Throwable t) { + LOG.error( + subscriber + + " violated the Reactive Streams rule 2.13 by throwing an exception from onNext.", + t); + cancel(); + } + } + + private void doOnComplete() { + try { + // Then we signal onComplete as per rules 1.2 and 1.5 + subscriber.onComplete(); + } catch (Throwable t) { + LOG.error( + subscriber + + " violated the Reactive Streams rule 2.13 by throwing an exception from onComplete.", + t); + } + // We need to consider this Subscription as cancelled as per rule 1.6 + cancel(); + } + + private void doOnError(@NonNull Throwable error) { + try { + // Then we signal the error downstream, as per rules 1.2 and 1.4. + subscriber.onError(error); + } catch (Throwable t) { + t.addSuppressed(error); + LOG.error( + subscriber + + " violated the Reactive Streams rule 2.13 by throwing an exception from onError.", + t); + } + // We need to consider this Subscription as cancelled as per rule 1.6 + cancel(); + } + + private void clear() { + // We don't need the elements anymore and should not hold references + // to them. + queue.clear(); + // As per 3.13, Subscription.cancel() MUST request the Publisher to + // eventually drop any references to the corresponding subscriber. + // Our own publishers do not keep references to this subscription, + // but downstream processors might do so, which is why we need to + // defensively clear the subscriber reference when we are done. + subscriber = null; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/data/geometry/DefaultGeometry.java b/core/src/main/java/com/datastax/dse/driver/internal/core/data/geometry/DefaultGeometry.java new file mode 100644 index 00000000000..9b1148dff69 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/data/geometry/DefaultGeometry.java @@ -0,0 +1,202 @@ +/* + * 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 com.datastax.dse.driver.internal.core.data.geometry; + +import com.datastax.dse.driver.api.core.data.geometry.Geometry; +import com.datastax.dse.driver.api.core.data.geometry.Point; +import com.datastax.oss.driver.shaded.guava.common.base.Preconditions; +import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableList; +import com.esri.core.geometry.GeometryException; +import com.esri.core.geometry.SpatialReference; +import com.esri.core.geometry.ogc.OGCGeometry; +import com.esri.core.geometry.ogc.OGCLineString; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.nio.ByteBuffer; +import net.jcip.annotations.Immutable; + +@Immutable +public abstract class DefaultGeometry implements Geometry, Serializable { + + private static final long serialVersionUID = 1L; + + /** + * Default spatial reference for Well Known Text / Well Known Binary. + * + *

4326 is the EPSG identifier of the World Geodetic System (WGS) in + * its later revision, WGS 84. + */ + public static final SpatialReference SPATIAL_REFERENCE_4326 = SpatialReference.create(4326); + + @NonNull + public static T fromOgcWellKnownText( + @NonNull String source, @NonNull Class klass) { + OGCGeometry geometry; + try { + geometry = OGCGeometry.fromText(source); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException(e.getMessage()); + } + validateType(geometry, klass); + return klass.cast(geometry); + } + + @NonNull + public static T fromOgcWellKnownBinary( + @NonNull ByteBuffer source, @NonNull Class klass) { + OGCGeometry geometry; + try { + geometry = OGCGeometry.fromBinary(source); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException(e.getMessage()); + } + validateType(geometry, klass); + return klass.cast(geometry); + } + + @NonNull + public static T fromOgcGeoJson( + @NonNull String source, @NonNull Class klass) { + OGCGeometry geometry; + try { + geometry = OGCGeometry.fromGeoJson(source); + } catch (Exception e) { + throw new IllegalArgumentException(e.getMessage()); + } + validateType(geometry, klass); + return klass.cast(geometry); + } + + private static void validateType(OGCGeometry geometry, Class klass) { + if (!geometry.getClass().equals(klass)) { + throw new IllegalArgumentException( + String.format( + "%s is not of type %s", geometry.getClass().getSimpleName(), klass.getSimpleName())); + } + } + + private final OGCGeometry ogcGeometry; + + protected DefaultGeometry(@NonNull OGCGeometry ogcGeometry) { + this.ogcGeometry = ogcGeometry; + Preconditions.checkNotNull(ogcGeometry); + validateOgcGeometry(ogcGeometry); + } + + private static void validateOgcGeometry(OGCGeometry geometry) { + try { + if (geometry.is3D()) { + throw new IllegalArgumentException(String.format("'%s' is not 2D", geometry.asText())); + } + if (!geometry.isSimple()) { + throw new IllegalArgumentException( + String.format( + "'%s' is not simple. Points and edges cannot self-intersect.", geometry.asText())); + } + } catch (GeometryException e) { + throw new IllegalArgumentException("Invalid geometry" + e.getMessage()); + } + } + + @NonNull + public static ImmutableList getPoints(@NonNull OGCLineString lineString) { + ImmutableList.Builder builder = ImmutableList.builder(); + for (int i = 0; i < lineString.numPoints(); i++) { + builder.add(new DefaultPoint(lineString.pointN(i))); + } + return builder.build(); + } + + protected static com.esri.core.geometry.Point toEsri(Point p) { + return new com.esri.core.geometry.Point(p.X(), p.Y()); + } + + @NonNull + public OGCGeometry getOgcGeometry() { + return ogcGeometry; + } + + @NonNull + public com.esri.core.geometry.Geometry getEsriGeometry() { + return ogcGeometry.getEsriGeometry(); + } + + @NonNull + @Override + public String asWellKnownText() { + return ogcGeometry.asText(); + } + + @NonNull + @Override + public ByteBuffer asWellKnownBinary() { + return WkbUtil.asLittleEndianBinary(ogcGeometry); + } + + @NonNull + @Override + public String asGeoJson() { + return ogcGeometry.asGeoJson(); + } + + @Override + public boolean contains(@NonNull Geometry other) { + Preconditions.checkNotNull(other); + if (other instanceof DefaultGeometry) { + DefaultGeometry defautlOther = (DefaultGeometry) other; + return getOgcGeometry().contains(defautlOther.getOgcGeometry()); + } + return false; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof DefaultGeometry)) { + return false; + } + DefaultGeometry that = (DefaultGeometry) o; + return this.getOgcGeometry().equals((Object) that.getOgcGeometry()); + } + + @Override + public int hashCode() { + // OGCGeometry subclasses do not overwrite Object.hashCode() + // while com.esri.core.geometry.Geometry subclasses usually do, + // so use these instead; this is consistent with equals + // because OGCGeometry.equals() actually compare between + // com.esri.core.geometry.Geometry objects + return getEsriGeometry().hashCode(); + } + + // Should never be called since we serialize a proxy (see subclasses) + @SuppressWarnings("UnusedVariable") + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Proxy required"); + } + + @Override + public String toString() { + return asWellKnownText(); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/data/geometry/DefaultLineString.java b/core/src/main/java/com/datastax/dse/driver/internal/core/data/geometry/DefaultLineString.java new file mode 100644 index 00000000000..1cf64bb366d --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/data/geometry/DefaultLineString.java @@ -0,0 +1,68 @@ +/* + * 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 com.datastax.dse.driver.internal.core.data.geometry; + +import com.datastax.dse.driver.api.core.data.geometry.LineString; +import com.datastax.dse.driver.api.core.data.geometry.Point; +import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableList; +import com.esri.core.geometry.Polyline; +import com.esri.core.geometry.ogc.OGCLineString; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.List; +import net.jcip.annotations.Immutable; + +@Immutable +public class DefaultLineString extends DefaultGeometry implements LineString { + + private static final long serialVersionUID = 1280189361978382248L; + + private static OGCLineString fromPoints(Point p1, Point p2, Point... pn) { + Polyline polyline = new Polyline(toEsri(p1), toEsri(p2)); + for (Point p : pn) { + polyline.lineTo(toEsri(p)); + } + return new OGCLineString(polyline, 0, DefaultGeometry.SPATIAL_REFERENCE_4326); + } + + private final List points; + + public DefaultLineString(@NonNull Point p1, @NonNull Point p2, @NonNull Point... pn) { + super(fromPoints(p1, p2, pn)); + this.points = ImmutableList.builder().add(p1).add(p2).add(pn).build(); + } + + public DefaultLineString(@NonNull OGCLineString lineString) { + super(lineString); + this.points = getPoints(lineString); + } + + @NonNull + @Override + public List getPoints() { + return points; + } + + /** + * This object gets replaced by an internal proxy for serialization. + * + * @serialData a single byte array containing the Well-Known Binary representation. + */ + private Object writeReplace() { + return new WkbSerializationProxy(this.asWellKnownBinary()); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/data/geometry/DefaultPoint.java b/core/src/main/java/com/datastax/dse/driver/internal/core/data/geometry/DefaultPoint.java new file mode 100644 index 00000000000..c9540b10d8a --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/data/geometry/DefaultPoint.java @@ -0,0 +1,64 @@ +/* + * 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 com.datastax.dse.driver.internal.core.data.geometry; + +import com.datastax.dse.driver.api.core.data.geometry.Point; +import com.esri.core.geometry.ogc.OGCPoint; +import edu.umd.cs.findbugs.annotations.NonNull; +import net.jcip.annotations.Immutable; + +@Immutable +public class DefaultPoint extends DefaultGeometry implements Point { + + private static final long serialVersionUID = -8337622213980781285L; + + public DefaultPoint(double x, double y) { + this( + new OGCPoint( + new com.esri.core.geometry.Point(x, y), DefaultGeometry.SPATIAL_REFERENCE_4326)); + } + + public DefaultPoint(@NonNull OGCPoint point) { + super(point); + } + + @NonNull + @Override + public OGCPoint getOgcGeometry() { + return (OGCPoint) super.getOgcGeometry(); + } + + @Override + public double X() { + return getOgcGeometry().X(); + } + + @Override + public double Y() { + return getOgcGeometry().Y(); + } + + /** + * This object gets replaced by an internal proxy for serialization. + * + * @serialData a single byte array containing the Well-Known Binary representation. + */ + private Object writeReplace() { + return new WkbSerializationProxy(this.asWellKnownBinary()); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/data/geometry/DefaultPolygon.java b/core/src/main/java/com/datastax/dse/driver/internal/core/data/geometry/DefaultPolygon.java new file mode 100644 index 00000000000..27d375d42b1 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/data/geometry/DefaultPolygon.java @@ -0,0 +1,131 @@ +/* + * 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 com.datastax.dse.driver.internal.core.data.geometry; + +import com.datastax.dse.driver.api.core.data.geometry.Point; +import com.datastax.dse.driver.api.core.data.geometry.Polygon; +import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableList; +import com.esri.core.geometry.Operator; +import com.esri.core.geometry.OperatorFactoryLocal; +import com.esri.core.geometry.OperatorSimplifyOGC; +import com.esri.core.geometry.ogc.OGCPolygon; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.Collections; +import java.util.List; +import net.jcip.annotations.Immutable; + +@Immutable +public class DefaultPolygon extends DefaultGeometry implements Polygon { + + private static final long serialVersionUID = 3694196802962890314L; + + private final List exteriorRing; + private final List> interiorRings; + + public DefaultPolygon( + @NonNull Point p1, @NonNull Point p2, @NonNull Point p3, @NonNull Point... pn) { + super(fromPoints(p1, p2, p3, pn)); + this.exteriorRing = ImmutableList.builder().add(p1).add(p2).add(p3).add(pn).build(); + this.interiorRings = Collections.emptyList(); + } + + public DefaultPolygon(@NonNull OGCPolygon polygon) { + super(polygon); + if (polygon.isEmpty()) { + this.exteriorRing = ImmutableList.of(); + } else { + this.exteriorRing = getPoints(polygon.exteriorRing()); + } + + ImmutableList.Builder> builder = ImmutableList.builder(); + for (int i = 0; i < polygon.numInteriorRing(); i++) { + builder.add(getPoints(polygon.interiorRingN(i))); + } + this.interiorRings = builder.build(); + } + + @NonNull + @Override + public List getExteriorRing() { + return exteriorRing; + } + + @NonNull + @Override + public List> getInteriorRings() { + return interiorRings; + } + + private static OGCPolygon fromPoints(Point p1, Point p2, Point p3, Point... pn) { + com.esri.core.geometry.Polygon polygon = new com.esri.core.geometry.Polygon(); + addPath(polygon, p1, p2, p3, pn); + return new OGCPolygon(simplify(polygon), DefaultGeometry.SPATIAL_REFERENCE_4326); + } + + private static void addPath( + com.esri.core.geometry.Polygon polygon, Point p1, Point p2, Point p3, Point[] pn) { + + polygon.startPath(toEsri(p1)); + polygon.lineTo(toEsri(p2)); + polygon.lineTo(toEsri(p3)); + for (Point p : pn) { + polygon.lineTo(toEsri(p)); + } + } + + private static com.esri.core.geometry.Polygon simplify(com.esri.core.geometry.Polygon polygon) { + OperatorSimplifyOGC op = + (OperatorSimplifyOGC) + OperatorFactoryLocal.getInstance().getOperator(Operator.Type.SimplifyOGC); + return (com.esri.core.geometry.Polygon) + op.execute(polygon, DefaultGeometry.SPATIAL_REFERENCE_4326, true, null); + } + + /** + * This object gets replaced by an internal proxy for serialization. + * + * @serialData a single byte array containing the Well-Known Binary representation. + */ + private Object writeReplace() { + return new WkbSerializationProxy(this.asWellKnownBinary()); + } + + public static class Builder implements Polygon.Builder { + private final com.esri.core.geometry.Polygon polygon = new com.esri.core.geometry.Polygon(); + + @NonNull + @Override + public Builder addRing( + @NonNull Point p1, @NonNull Point p2, @NonNull Point p3, @NonNull Point... pn) { + addPath(polygon, p1, p2, p3, pn); + return this; + } + + /** + * Builds the polygon. + * + * @return the polygon. + */ + @NonNull + @Override + public Polygon build() { + return new DefaultPolygon( + new OGCPolygon(simplify(polygon), DefaultGeometry.SPATIAL_REFERENCE_4326)); + } + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/data/geometry/Distance.java b/core/src/main/java/com/datastax/dse/driver/internal/core/data/geometry/Distance.java new file mode 100644 index 00000000000..518f6aa1346 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/data/geometry/Distance.java @@ -0,0 +1,237 @@ +/* + * 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 com.datastax.dse.driver.internal.core.data.geometry; + +import static java.util.regex.Pattern.CASE_INSENSITIVE; + +import com.datastax.dse.driver.api.core.data.geometry.Geometry; +import com.datastax.dse.driver.api.core.data.geometry.LineString; +import com.datastax.dse.driver.api.core.data.geometry.Point; +import com.datastax.dse.driver.api.core.data.geometry.Polygon; +import com.datastax.dse.driver.api.core.graph.predicates.Geo; +import com.datastax.oss.driver.shaded.guava.common.base.Preconditions; +import com.esri.core.geometry.MultiPath; +import com.esri.core.geometry.ogc.OGCGeometry; +import com.esri.core.geometry.ogc.OGCPoint; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.nio.ByteBuffer; +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import net.jcip.annotations.Immutable; + +/** + * The driver-side representation of DSE's {@code Geo.distance}. + * + *

This is a circle in a two-dimensional XY plane represented by its center point and radius. It + * is used as a search criteria to determine whether or not another geospatial object lies within a + * circular area. + * + *

Note that this shape has no equivalent in the OGC and GeoJSON standards: as a consequence, + * {@link #asWellKnownText()} returns a custom format, and {@link #getOgcGeometry()}, {@link + * #asWellKnownBinary()}, and {@link #asGeoJson()} throw {@link UnsupportedOperationException}. + * + *

Unlike other geo types, this class is never exposed directly to driver clients: it is used + * internally by {@linkplain Geo#inside(Point, double) geo predicates}, but cannot be a column type, + * nor appear in CQL or graph results. Therefore it doesn't have a public-facing interface, nor a + * built-in codec. + */ +@Immutable +public class Distance extends DefaultGeometry { + + private static final Pattern WKT_PATTERN = + Pattern.compile( + "distance *\\( *\\( *([\\d\\.-]+) *([\\d+\\.-]+) *\\) *([\\d+\\.-]+) *\\)", + CASE_INSENSITIVE); + + /** + * Creates a distance from its Well-known + * Text (WKT) representation. + * + * @param source the Well-known Text representation to parse. + * @return the point represented by the WKT. + * @throws IllegalArgumentException if the string does not contain a valid Well-known Text + * representation. + * @see Distance#asWellKnownText() + */ + @NonNull + public static Distance fromWellKnownText(@NonNull String source) { + Matcher matcher = WKT_PATTERN.matcher(source.trim()); + if (matcher.matches() && matcher.groupCount() == 3) { + try { + return new Distance( + new DefaultPoint( + Double.parseDouble(matcher.group(1)), Double.parseDouble(matcher.group(2))), + Double.parseDouble(matcher.group(3))); + } catch (NumberFormatException var3) { + throw new IllegalArgumentException(String.format("Unable to parse %s", source)); + } + } else { + throw new IllegalArgumentException(String.format("Unable to parse %s", source)); + } + } + + private final DefaultPoint center; + + private final double radius; + + /** + * Creates a new distance with the given center and radius. + * + * @param center The center point. + * @param radius The radius of the circle representing distance. + */ + public Distance(@NonNull Point center, double radius) { + super(((DefaultPoint) center).getOgcGeometry()); + Preconditions.checkNotNull(center); + Preconditions.checkArgument(radius >= 0.0D, "Radius must be >= 0 (got %s)", radius); + this.center = ((DefaultPoint) center); + this.radius = radius; + } + + /** @return The center point of the circle representing this distance. */ + @NonNull + public Point getCenter() { + return center; + } + + /** @return The radius of the circle representing this distance. */ + public double getRadius() { + return radius; + } + + /** + * Returns a Well-known Text (WKT) + * representation of this geospatial type. + * + *

Since there is no Well-known Text specification for Distance, this returns a custom format + * of: DISTANCE((center.x center.y) radius) + * + * @return a Well-known Text representation of this object. + */ + @NonNull + @Override + public String asWellKnownText() { + return String.format("DISTANCE((%s %s) %s)", this.center.X(), this.center.Y(), this.radius); + } + + /** + * The distance type has no equivalent in the OGC standard: this method throws an {@link + * UnsupportedOperationException}. + */ + @NonNull + @Override + public OGCGeometry getOgcGeometry() { + throw new UnsupportedOperationException(); + } + + /** + * The distance type has no equivalent in the OGC standard: this method throws an {@link + * UnsupportedOperationException}. + */ + @NonNull + @Override + public ByteBuffer asWellKnownBinary() { + throw new UnsupportedOperationException(); + } + + /** + * The distance type has no equivalent in the GeoJSON standard: this method throws an {@link + * UnsupportedOperationException}. + */ + @Override + @NonNull + public String asGeoJson() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } else if (other instanceof Distance) { + Distance that = (Distance) other; + return Objects.equals(this.center, that.center) && this.radius == that.radius; + } else { + return false; + } + } + + @Override + public int hashCode() { + return Objects.hash(center, radius); + } + + @SuppressWarnings("SimplifiableConditionalExpression") + @Override + public boolean contains(@NonNull Geometry geometry) { + return geometry instanceof Distance + ? this.containsDistance((Distance) geometry) + : geometry instanceof Point + ? this.containsPoint((Point) geometry) + : geometry instanceof LineString + ? this.containsLineString((LineString) geometry) + : geometry instanceof Polygon ? this.containsPolygon((Polygon) geometry) : false; + } + + private boolean containsDistance(Distance distance) { + return this.center.getOgcGeometry().distance(distance.center.getOgcGeometry()) + distance.radius + <= this.radius; + } + + private boolean containsPoint(Point point) { + return this.containsOGCPoint(((DefaultPoint) point).getOgcGeometry()); + } + + private boolean containsLineString(LineString lineString) { + MultiPath multiPath = + (MultiPath) ((DefaultLineString) lineString).getOgcGeometry().getEsriGeometry(); + return containsMultiPath(multiPath); + } + + private boolean containsPolygon(Polygon polygon) { + MultiPath multiPath = + (com.esri.core.geometry.Polygon) + ((DefaultPolygon) polygon).getOgcGeometry().getEsriGeometry(); + return containsMultiPath(multiPath); + } + + private boolean containsMultiPath(MultiPath multiPath) { + int numPoints = multiPath.getPointCount(); + for (int i = 0; i < numPoints; ++i) { + OGCPoint point = new OGCPoint(multiPath.getPoint(i), DefaultGeometry.SPATIAL_REFERENCE_4326); + if (!this.containsOGCPoint(point)) { + return false; + } + } + return true; + } + + private boolean containsOGCPoint(OGCPoint point) { + return this.center.getOgcGeometry().distance(point) <= this.radius; + } + + /** + * This object gets replaced by an internal proxy for serialization. + * + * @serialData Point (wkb) for center followed by double for radius + */ + private Object writeReplace() { + return new DistanceSerializationProxy(this); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/data/geometry/DistanceSerializationProxy.java b/core/src/main/java/com/datastax/dse/driver/internal/core/data/geometry/DistanceSerializationProxy.java new file mode 100644 index 00000000000..515af121980 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/data/geometry/DistanceSerializationProxy.java @@ -0,0 +1,43 @@ +/* + * 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 com.datastax.dse.driver.internal.core.data.geometry; + +import com.datastax.dse.driver.api.core.data.geometry.Point; +import java.io.Serializable; + +/** + * A thin wrapper around {@link Distance}, that gets substituted during the serialization / + * deserialization process. This allows {@link Distance} to be immutable and reference centers' OGC + * counterpart. + */ +public class DistanceSerializationProxy implements Serializable { + + private static final long serialVersionUID = 1L; + + private final Point center; + private final double radius; + + public DistanceSerializationProxy(Distance distance) { + this.center = distance.getCenter(); + this.radius = distance.getRadius(); + } + + private Object readResolve() { + return new Distance(center, radius); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/data/geometry/WkbSerializationProxy.java b/core/src/main/java/com/datastax/dse/driver/internal/core/data/geometry/WkbSerializationProxy.java new file mode 100644 index 00000000000..92c0f6de2d5 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/data/geometry/WkbSerializationProxy.java @@ -0,0 +1,60 @@ +/* + * 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 com.datastax.dse.driver.internal.core.data.geometry; + +import com.datastax.dse.driver.api.core.data.geometry.LineString; +import com.datastax.dse.driver.api.core.data.geometry.Point; +import com.datastax.dse.driver.api.core.data.geometry.Polygon; +import com.datastax.oss.protocol.internal.util.Bytes; +import java.io.Serializable; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import net.jcip.annotations.Immutable; + +/** + * A thin wrapper around a Well-Known Binary byte sequence, that gets substituted for {@link + * DefaultGeometry} instances during the serialization / deserialization process. This allows + * immutable geometry classes. + */ +@Immutable +class WkbSerializationProxy implements Serializable { + + private static final long serialVersionUID = 1L; + + private final byte[] wkb; + + WkbSerializationProxy(ByteBuffer wkb) { + this.wkb = Bytes.getArray(wkb); + } + + private Object readResolve() { + ByteBuffer buffer = ByteBuffer.wrap(wkb).order(ByteOrder.nativeOrder()); + int type = buffer.getInt(1); + + if (type == 1) { + return Point.fromWellKnownBinary(buffer); + } else if (type == 2) { + return LineString.fromWellKnownBinary(buffer); + } else if (type == 3) { + return Polygon.fromWellKnownBinary(buffer); + } else { + throw new IllegalArgumentException( + "Unknown geospatial type code in serialized form: " + type); + } + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/data/geometry/WkbUtil.java b/core/src/main/java/com/datastax/dse/driver/internal/core/data/geometry/WkbUtil.java new file mode 100644 index 00000000000..3f18b32fda2 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/data/geometry/WkbUtil.java @@ -0,0 +1,120 @@ +/* + * 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 com.datastax.dse.driver.internal.core.data.geometry; + +import com.esri.core.geometry.Geometry; +import com.esri.core.geometry.Operator; +import com.esri.core.geometry.OperatorExportToWkb; +import com.esri.core.geometry.OperatorFactoryLocal; +import com.esri.core.geometry.WkbExportFlags; +import com.esri.core.geometry.ogc.OGCGeometry; +import com.esri.core.geometry.ogc.OGCLineString; +import com.esri.core.geometry.ogc.OGCPoint; +import com.esri.core.geometry.ogc.OGCPolygon; +import java.lang.reflect.Method; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** + * Helper class to serialize OGC geometries to Well-Known Binary, forcing the byte order to little + * endian. + * + *

WKB encodes the byte order, so in theory we could send the buffer in any order, even if it is + * different from the server. However DSE server performs an additional validation step server-side: + * it deserializes to Java, serializes back to WKB, and then compares the original buffer to the + * "re-serialized" one. If they don't match, a MarshalException is thrown. So with a client in + * big-endian and a server in little-endian, we would get: + * + *

+ * incoming buffer (big endian) --> Java --> reserialized buffer (little endian)
+ * 
+ * + * Since the two buffers have a different endian-ness, they don't match. + * + *

The ESRI library defaults to the native byte order and doesn't let us change it. Therefore: + * + *

    + *
  • if the native order is little endian (vast majority of cases), this class simply delegates + * to the appropriate public API method; + *
  • if the native order is big endian, it re-implements the serialization code, using + * reflection to get access to a private method. If reflection fails for any reason (updated + * ESRI library, security manager...), a runtime exception will be thrown. + *
+ */ +class WkbUtil { + + private static final boolean IS_NATIVE_LITTLE_ENDIAN = + ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN) + && System.getProperty("com.datastax.driver.dse.geometry.FORCE_REFLECTION_WKB") + == null; // only for tests + + static ByteBuffer asLittleEndianBinary(OGCGeometry ogcGeometry) { + if (IS_NATIVE_LITTLE_ENDIAN) { + return ogcGeometry.asBinary(); // the default implementation does what we want + } else { + int exportFlags; + if (ogcGeometry instanceof OGCPoint) { + exportFlags = 0; + } else if (ogcGeometry instanceof OGCLineString) { + exportFlags = WkbExportFlags.wkbExportLineString; + } else if (ogcGeometry instanceof OGCPolygon) { + exportFlags = WkbExportFlags.wkbExportPolygon; + } else { + throw new AssertionError("Unsupported type: " + ogcGeometry.getClass()); + } + + // Copy-pasted from OperatorExportToWkbLocal#execute, except for the flags and order + int size = exportToWKB(exportFlags, ogcGeometry.getEsriGeometry(), null); + ByteBuffer wkbBuffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN); + exportToWKB(exportFlags, ogcGeometry.getEsriGeometry(), wkbBuffer); + return wkbBuffer; + } + } + + // Provides reflective access to the private static method OperatorExportToWkbLocal#exportToWKB + private static int exportToWKB(int exportFlags, Geometry geometry, ByteBuffer wkbBuffer) { + assert !IS_NATIVE_LITTLE_ENDIAN; + try { + return (Integer) exportToWKB.invoke(null, exportFlags, geometry, wkbBuffer); + } catch (Exception e) { + throw new RuntimeException( + "Couldn't invoke private method OperatorExportToWkbLocal#exportToWKB", e); + } + } + + private static final Method exportToWKB; + + static { + if (IS_NATIVE_LITTLE_ENDIAN) { + exportToWKB = null; // won't be used + } else { + try { + OperatorExportToWkb op = + (OperatorExportToWkb) + OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ExportToWkb); + exportToWKB = + op.getClass() + .getDeclaredMethod("exportToWKB", int.class, Geometry.class, ByteBuffer.class); + exportToWKB.setAccessible(true); + } catch (NoSuchMethodException e) { + throw new RuntimeException( + "Couldn't get access to private method OperatorExportToWkbLocal#exportToWKB", e); + } + } + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/ByteBufUtil.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/ByteBufUtil.java new file mode 100644 index 00000000000..333ba6099d3 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/ByteBufUtil.java @@ -0,0 +1,47 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import java.nio.ByteBuffer; + +public class ByteBufUtil { + + // Does not move the reader index of the ByteBuf parameter + public static ByteBuffer toByteBuffer(ByteBuf buffer) { + if (buffer.isDirect()) { + return buffer.nioBuffer(); + } + final byte[] bytes = new byte[buffer.readableBytes()]; + buffer.getBytes(buffer.readerIndex(), bytes); + return ByteBuffer.wrap(bytes); + } + + static ByteBuf toByteBuf(ByteBuffer buffer) { + return Unpooled.wrappedBuffer(buffer); + } + + // read a predefined amount of bytes from the netty buffer and move its readerIndex + public static ByteBuffer readBytes(ByteBuf nettyBuf, int size) { + ByteBuffer res = ByteBuffer.allocate(size); + nettyBuf.readBytes(res); + res.flip(); + return res; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/BytecodeGraphStatement.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/BytecodeGraphStatement.java new file mode 100644 index 00000000000..b6fe05a987c --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/BytecodeGraphStatement.java @@ -0,0 +1,130 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph; + +import com.datastax.dse.driver.api.core.graph.FluentGraphStatement; +import com.datastax.oss.driver.api.core.ConsistencyLevel; +import com.datastax.oss.driver.api.core.config.DriverExecutionProfile; +import com.datastax.oss.driver.api.core.cql.Statement; +import com.datastax.oss.driver.api.core.metadata.Node; +import java.nio.ByteBuffer; +import java.time.Duration; +import java.util.Collections; +import java.util.Map; +import org.apache.tinkerpop.gremlin.process.traversal.Bytecode; + +/** + * A dedicated statement implementation for implicit traversal execution via a {@link + * DseGraphRemoteConnection}. + * + *

This is a simplified version of {@link FluentGraphStatement} that exposes the bytecode + * directly instead of the traversal. + * + *

This class is for internal use only. + */ +public class BytecodeGraphStatement extends GraphStatementBase { + + private final Bytecode bytecode; + + public BytecodeGraphStatement( + Bytecode bytecode, DriverExecutionProfile executionProfile, String executionProfileName) { + this( + bytecode, + null, + null, + null, + Statement.NO_DEFAULT_TIMESTAMP, + executionProfile, + executionProfileName, + Collections.emptyMap(), + null, + null, + null, + null, + null, + null); + } + + private BytecodeGraphStatement( + Bytecode bytecode, + Boolean isIdempotent, + Duration timeout, + Node node, + long timestamp, + DriverExecutionProfile executionProfile, + String executionProfileName, + Map customPayload, + String graphName, + String traversalSource, + String subProtocol, + ConsistencyLevel consistencyLevel, + ConsistencyLevel readConsistencyLevel, + ConsistencyLevel writeConsistencyLevel) { + super( + isIdempotent, + timeout, + node, + timestamp, + executionProfile, + executionProfileName, + customPayload, + graphName, + traversalSource, + subProtocol, + consistencyLevel, + readConsistencyLevel, + writeConsistencyLevel); + this.bytecode = bytecode; + } + + public Bytecode getBytecode() { + return bytecode; + } + + @Override + protected BytecodeGraphStatement newInstance( + Boolean isIdempotent, + Duration timeout, + Node node, + long timestamp, + DriverExecutionProfile executionProfile, + String executionProfileName, + Map customPayload, + String graphName, + String traversalSource, + String subProtocol, + ConsistencyLevel consistencyLevel, + ConsistencyLevel readConsistencyLevel, + ConsistencyLevel writeConsistencyLevel) { + return new BytecodeGraphStatement( + bytecode, + isIdempotent, + timeout, + node, + timestamp, + executionProfile, + executionProfileName, + customPayload, + graphName, + traversalSource, + subProtocol, + consistencyLevel, + readConsistencyLevel, + writeConsistencyLevel); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/ContinuousAsyncGraphResultSet.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/ContinuousAsyncGraphResultSet.java new file mode 100644 index 00000000000..9c7f773c3a2 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/ContinuousAsyncGraphResultSet.java @@ -0,0 +1,151 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph; + +import com.datastax.dse.driver.api.core.graph.AsyncGraphResultSet; +import com.datastax.dse.driver.api.core.graph.GraphNode; +import com.datastax.oss.driver.api.core.cql.ExecutionInfo; +import com.datastax.oss.driver.internal.core.util.CountingIterator; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.Collections; +import java.util.Queue; +import java.util.concurrent.CompletionStage; +import net.jcip.annotations.NotThreadSafe; + +@NotThreadSafe // wraps a mutable queue +public class ContinuousAsyncGraphResultSet implements AsyncGraphResultSet { + + private final CountingIterator iterator; + private final int pageNumber; + private final boolean hasMorePages; + private final ExecutionInfo executionInfo; + private final ContinuousGraphRequestHandler continuousGraphRequestHandler; + private final Iterable currentPage; + + public ContinuousAsyncGraphResultSet( + ExecutionInfo executionInfo, + Queue data, + int pageNumber, + boolean hasMorePages, + ContinuousGraphRequestHandler continuousGraphRequestHandler, + GraphProtocol graphProtocol) { + + this.iterator = new GraphResultIterator(data, graphProtocol); + this.pageNumber = pageNumber; + this.hasMorePages = hasMorePages; + this.executionInfo = executionInfo; + this.continuousGraphRequestHandler = continuousGraphRequestHandler; + this.currentPage = () -> iterator; + } + + @NonNull + @Override + public ExecutionInfo getRequestExecutionInfo() { + return executionInfo; + } + + @NonNull + @Override + @Deprecated + public com.datastax.dse.driver.api.core.graph.GraphExecutionInfo getExecutionInfo() { + return GraphExecutionInfoConverter.convert(executionInfo); + } + + @Override + public int remaining() { + return iterator.remaining(); + } + + @NonNull + @Override + public Iterable currentPage() { + return currentPage; + } + + @Override + public boolean hasMorePages() { + return hasMorePages; + } + + @NonNull + @Override + public CompletionStage fetchNextPage() throws IllegalStateException { + if (!hasMorePages()) { + throw new IllegalStateException( + "Can't call fetchNextPage() on the last page (use hasMorePages() to check)"); + } + return continuousGraphRequestHandler.fetchNextPage(); + } + + @Override + public void cancel() { + continuousGraphRequestHandler.cancel(); + } + + /** Returns the current page's number. Pages are numbered starting from 1. */ + public int pageNumber() { + return pageNumber; + } + + static AsyncGraphResultSet empty(ExecutionInfo executionInfo) { + + return new AsyncGraphResultSet() { + + @NonNull + @Override + public ExecutionInfo getRequestExecutionInfo() { + return executionInfo; + } + + @NonNull + @Override + @Deprecated + public com.datastax.dse.driver.api.core.graph.GraphExecutionInfo getExecutionInfo() { + return GraphExecutionInfoConverter.convert(executionInfo); + } + + @NonNull + @Override + public Iterable currentPage() { + return Collections.emptyList(); + } + + @Override + public int remaining() { + return 0; + } + + @Override + public boolean hasMorePages() { + return false; + } + + @NonNull + @Override + public CompletionStage fetchNextPage() throws IllegalStateException { + throw new IllegalStateException( + "Can't call fetchNextPage() on the last page (use hasMorePages() to check)"); + } + + @Override + public void cancel() { + // noop + } + }; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/ContinuousGraphRequestHandler.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/ContinuousGraphRequestHandler.java new file mode 100644 index 00000000000..07d9e4c84a3 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/ContinuousGraphRequestHandler.java @@ -0,0 +1,191 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph; + +import com.datastax.dse.driver.api.core.config.DseDriverOption; +import com.datastax.dse.driver.api.core.graph.AsyncGraphResultSet; +import com.datastax.dse.driver.api.core.graph.GraphNode; +import com.datastax.dse.driver.api.core.graph.GraphStatement; +import com.datastax.dse.driver.api.core.metrics.DseNodeMetric; +import com.datastax.dse.driver.api.core.metrics.DseSessionMetric; +import com.datastax.dse.driver.internal.core.cql.continuous.ContinuousRequestHandlerBase; +import com.datastax.dse.driver.internal.core.graph.binary.GraphBinaryModule; +import com.datastax.dse.protocol.internal.response.result.DseRowsMetadata; +import com.datastax.oss.driver.api.core.config.DriverExecutionProfile; +import com.datastax.oss.driver.api.core.cql.ColumnDefinitions; +import com.datastax.oss.driver.api.core.cql.ExecutionInfo; +import com.datastax.oss.driver.internal.core.context.InternalDriverContext; +import com.datastax.oss.driver.internal.core.cql.Conversions; +import com.datastax.oss.driver.internal.core.session.DefaultSession; +import com.datastax.oss.driver.shaded.guava.common.base.MoreObjects; +import com.datastax.oss.protocol.internal.Message; +import com.datastax.oss.protocol.internal.response.result.Rows; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.time.Duration; +import java.util.ArrayDeque; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import net.jcip.annotations.ThreadSafe; + +/** + * Handles a Graph request that supports multiple response messages (a.k.a. continuous paging + * request). + */ +@ThreadSafe +public class ContinuousGraphRequestHandler + extends ContinuousRequestHandlerBase, AsyncGraphResultSet> { + + private final GraphBinaryModule graphBinaryModule; + private final GraphSupportChecker graphSupportChecker; + private final Duration globalTimeout; + + ContinuousGraphRequestHandler( + @NonNull GraphStatement statement, + @NonNull DefaultSession session, + @NonNull InternalDriverContext context, + @NonNull String sessionLogPrefix, + @NonNull GraphBinaryModule graphBinaryModule, + @NonNull GraphSupportChecker graphSupportChecker) { + super( + statement, + session, + context, + sessionLogPrefix, + AsyncGraphResultSet.class, + true, + DseSessionMetric.GRAPH_CLIENT_TIMEOUTS, + DseSessionMetric.GRAPH_REQUESTS, + DseNodeMetric.GRAPH_MESSAGES); + this.graphBinaryModule = graphBinaryModule; + this.graphSupportChecker = graphSupportChecker; + DriverExecutionProfile executionProfile = + Conversions.resolveExecutionProfile(statement, context); + globalTimeout = + MoreObjects.firstNonNull( + statement.getTimeout(), + executionProfile.getDuration(DseDriverOption.GRAPH_TIMEOUT, Duration.ZERO)); + // NOTE that ordering of the following statement matters. + // We should register this request after all fields have been initialized. + throttler.register(this); + } + + @NonNull + @Override + protected Duration getGlobalTimeout() { + return globalTimeout; + } + + @NonNull + @Override + protected Duration getPageTimeout(@NonNull GraphStatement statement, int pageNumber) { + return Duration.ZERO; + } + + @NonNull + @Override + protected Duration getReviseRequestTimeout(@NonNull GraphStatement statement) { + return Duration.ZERO; + } + + @Override + protected int getMaxEnqueuedPages(@NonNull GraphStatement statement) { + DriverExecutionProfile executionProfile = + Conversions.resolveExecutionProfile(statement, context); + return executionProfile.getInt(DseDriverOption.GRAPH_CONTINUOUS_PAGING_MAX_ENQUEUED_PAGES); + } + + @Override + protected int getMaxPages(@NonNull GraphStatement statement) { + DriverExecutionProfile executionProfile = + Conversions.resolveExecutionProfile(statement, context); + return executionProfile.getInt(DseDriverOption.GRAPH_CONTINUOUS_PAGING_MAX_PAGES); + } + + @NonNull + @Override + protected Message getMessage(@NonNull GraphStatement statement) { + DriverExecutionProfile executionProfile = + Conversions.resolveExecutionProfile(statement, context); + GraphProtocol subProtocol = + graphSupportChecker.inferGraphProtocol(statement, executionProfile, context); + return GraphConversions.createContinuousMessageFromGraphStatement( + statement, subProtocol, executionProfile, context, graphBinaryModule); + } + + @Override + protected boolean isTracingEnabled(@NonNull GraphStatement statement) { + return statement.isTracing(); + } + + @NonNull + @Override + protected Map createPayload(@NonNull GraphStatement statement) { + DriverExecutionProfile executionProfile = + Conversions.resolveExecutionProfile(statement, context); + GraphProtocol subProtocol = + graphSupportChecker.inferGraphProtocol(statement, executionProfile, context); + return GraphConversions.createCustomPayload( + statement, subProtocol, executionProfile, context, graphBinaryModule); + } + + @NonNull + @Override + protected AsyncGraphResultSet createEmptyResultSet(@NonNull ExecutionInfo executionInfo) { + return ContinuousAsyncGraphResultSet.empty(executionInfo); + } + + @NonNull + @Override + protected ContinuousAsyncGraphResultSet createResultSet( + @NonNull GraphStatement statement, + @NonNull Rows rows, + @NonNull ExecutionInfo executionInfo, + @NonNull ColumnDefinitions columnDefinitions) + throws IOException { + DriverExecutionProfile executionProfile = + Conversions.resolveExecutionProfile(statement, context); + GraphProtocol subProtocol = + graphSupportChecker.inferGraphProtocol(statement, executionProfile, context); + + Queue graphNodes = new ArrayDeque<>(); + for (List row : rows.getData()) { + if (subProtocol.isGraphBinary()) { + graphNodes.offer(GraphConversions.createGraphBinaryGraphNode(row, this.graphBinaryModule)); + } else { + graphNodes.offer(GraphSONUtils.createGraphNode(row, subProtocol)); + } + } + + DseRowsMetadata metadata = (DseRowsMetadata) rows.getMetadata(); + return new ContinuousAsyncGraphResultSet( + executionInfo, + graphNodes, + metadata.continuousPageNumber, + !metadata.isLastContinuousPage, + this, + subProtocol); + } + + @Override + protected int pageNumber(@NonNull AsyncGraphResultSet resultSet) { + return ((ContinuousAsyncGraphResultSet) resultSet).pageNumber(); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/CqlCollectionPredicate.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/CqlCollectionPredicate.java new file mode 100644 index 00000000000..349321da0cf --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/CqlCollectionPredicate.java @@ -0,0 +1,75 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph; + +import com.datastax.oss.driver.shaded.guava.common.base.Preconditions; +import java.util.Collection; +import java.util.Map; +import java.util.Objects; +import org.javatuples.Pair; + +/** Predicates that can be used on CQL Collections. */ +public enum CqlCollectionPredicate implements DsePredicate { + contains { + @Override + public boolean test(Object value, Object condition) { + preEvaluate(condition); + Preconditions.checkArgument(value instanceof Collection); + return ((Collection) value).contains(condition); + } + }, + + containsKey { + @Override + public boolean test(Object value, Object condition) { + preEvaluate(condition); + Preconditions.checkArgument(value instanceof Map); + return ((Map) value).containsKey(condition); + } + }, + + containsValue { + @Override + public boolean test(Object value, Object condition) { + preEvaluate(condition); + Preconditions.checkArgument(value instanceof Map); + return ((Map) value).containsValue(condition); + } + }, + + entryEq { + @Override + public boolean test(Object value, Object condition) { + preEvaluate(condition); + Preconditions.checkArgument(condition instanceof Pair); + Preconditions.checkArgument(value instanceof Map); + Pair pair = (Pair) condition; + Map map = (Map) value; + return Objects.equals(map.get(pair.getValue0()), pair.getValue1()); + } + }; + + @Override + public boolean isValidCondition(Object condition) { + if (condition instanceof Pair) { + Pair pair = (Pair) condition; + return pair.getValue0() != null && pair.getValue1() != null; + } + return condition != null; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/DefaultAsyncGraphResultSet.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/DefaultAsyncGraphResultSet.java new file mode 100644 index 00000000000..abc7cc9514e --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/DefaultAsyncGraphResultSet.java @@ -0,0 +1,83 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph; + +import com.datastax.dse.driver.api.core.graph.AsyncGraphResultSet; +import com.datastax.dse.driver.api.core.graph.GraphNode; +import com.datastax.oss.driver.api.core.cql.ExecutionInfo; +import com.datastax.oss.driver.internal.core.util.CountingIterator; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.Queue; +import java.util.concurrent.CompletionStage; +import net.jcip.annotations.NotThreadSafe; + +@NotThreadSafe // wraps a mutable queue +public class DefaultAsyncGraphResultSet implements AsyncGraphResultSet { + + private final ExecutionInfo executionInfo; + private final CountingIterator iterator; + private final Iterable currentPage; + + public DefaultAsyncGraphResultSet( + ExecutionInfo executionInfo, Queue data, GraphProtocol graphProtocol) { + this.executionInfo = executionInfo; + this.iterator = new GraphResultIterator(data, graphProtocol); + this.currentPage = () -> iterator; + } + + @NonNull + @Override + public ExecutionInfo getRequestExecutionInfo() { + return executionInfo; + } + + @NonNull + @Override + @Deprecated + public com.datastax.dse.driver.api.core.graph.GraphExecutionInfo getExecutionInfo() { + return GraphExecutionInfoConverter.convert(executionInfo); + } + + @Override + public int remaining() { + return iterator.remaining(); + } + + @NonNull + @Override + public Iterable currentPage() { + return currentPage; + } + + @Override + public boolean hasMorePages() { + return false; + } + + @NonNull + @Override + public CompletionStage fetchNextPage() throws IllegalStateException { + throw new IllegalStateException( + "No next page. Use #hasMorePages before calling this method to avoid this error."); + } + + @Override + public void cancel() { + // nothing to do + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/DefaultBatchGraphStatement.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/DefaultBatchGraphStatement.java new file mode 100644 index 00000000000..e16287c415d --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/DefaultBatchGraphStatement.java @@ -0,0 +1,154 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph; + +import com.datastax.dse.driver.api.core.graph.BatchGraphStatement; +import com.datastax.oss.driver.api.core.ConsistencyLevel; +import com.datastax.oss.driver.api.core.config.DriverExecutionProfile; +import com.datastax.oss.driver.api.core.metadata.Node; +import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableList; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.nio.ByteBuffer; +import java.time.Duration; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import net.jcip.annotations.Immutable; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; + +@Immutable +public class DefaultBatchGraphStatement extends GraphStatementBase + implements BatchGraphStatement { + + private final List traversals; + + public DefaultBatchGraphStatement( + Iterable traversals, + Boolean isIdempotent, + Duration timeout, + Node node, + long timestamp, + DriverExecutionProfile executionProfile, + String executionProfileName, + Map customPayload, + String graphName, + String traversalSource, + String subProtocol, + ConsistencyLevel consistencyLevel, + ConsistencyLevel readConsistencyLevel, + ConsistencyLevel writeConsistencyLevel) { + super( + isIdempotent, + timeout, + node, + timestamp, + executionProfile, + executionProfileName, + customPayload, + graphName, + traversalSource, + subProtocol, + consistencyLevel, + readConsistencyLevel, + writeConsistencyLevel); + this.traversals = ImmutableList.copyOf(traversals); + } + + @NonNull + @Override + public DefaultBatchGraphStatement addTraversal(@NonNull GraphTraversal newTraversal) { + return new DefaultBatchGraphStatement( + ImmutableList.builder().addAll(traversals).add(newTraversal).build(), + isIdempotent(), + getTimeout(), + getNode(), + getTimestamp(), + getExecutionProfile(), + getExecutionProfileName(), + getCustomPayload(), + getGraphName(), + getTraversalSource(), + getSubProtocol(), + getConsistencyLevel(), + getReadConsistencyLevel(), + getWriteConsistencyLevel()); + } + + @NonNull + @Override + public DefaultBatchGraphStatement addTraversals(@NonNull Iterable newTraversals) { + return new DefaultBatchGraphStatement( + ImmutableList.builder().addAll(traversals).addAll(newTraversals).build(), + isIdempotent(), + getTimeout(), + getNode(), + getTimestamp(), + getExecutionProfile(), + getExecutionProfileName(), + getCustomPayload(), + getGraphName(), + getTraversalSource(), + getSubProtocol(), + getConsistencyLevel(), + getReadConsistencyLevel(), + getWriteConsistencyLevel()); + } + + @Override + public int size() { + return this.traversals.size(); + } + + @Override + protected BatchGraphStatement newInstance( + Boolean isIdempotent, + Duration timeout, + Node node, + long timestamp, + DriverExecutionProfile executionProfile, + String executionProfileName, + Map customPayload, + String graphName, + String traversalSource, + String subProtocol, + ConsistencyLevel consistencyLevel, + ConsistencyLevel readConsistencyLevel, + ConsistencyLevel writeConsistencyLevel) { + return new DefaultBatchGraphStatement( + traversals, + isIdempotent, + timeout, + node, + timestamp, + executionProfile, + executionProfileName, + customPayload, + graphName, + traversalSource, + subProtocol, + consistencyLevel, + readConsistencyLevel, + writeConsistencyLevel); + } + + @NonNull + @Override + public Iterator iterator() { + return this.traversals.iterator(); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/DefaultDseRemoteConnectionBuilder.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/DefaultDseRemoteConnectionBuilder.java new file mode 100644 index 00000000000..146e8e17ea2 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/DefaultDseRemoteConnectionBuilder.java @@ -0,0 +1,54 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph; + +import com.datastax.dse.driver.api.core.graph.DseGraphRemoteConnectionBuilder; +import com.datastax.oss.driver.api.core.CqlSession; +import com.datastax.oss.driver.api.core.config.DriverExecutionProfile; +import net.jcip.annotations.NotThreadSafe; +import org.apache.tinkerpop.gremlin.process.remote.RemoteConnection; + +@NotThreadSafe +public class DefaultDseRemoteConnectionBuilder implements DseGraphRemoteConnectionBuilder { + + private final CqlSession session; + private DriverExecutionProfile executionProfile; + private String executionProfileName; + + public DefaultDseRemoteConnectionBuilder(CqlSession session) { + this.session = session; + } + + @Override + public RemoteConnection build() { + return new DseGraphRemoteConnection(session, executionProfile, executionProfileName); + } + + @Override + public DseGraphRemoteConnectionBuilder withExecutionProfile( + DriverExecutionProfile executionProfile) { + this.executionProfile = executionProfile; + return this; + } + + @Override + public DseGraphRemoteConnectionBuilder withExecutionProfileName(String executionProfileName) { + this.executionProfileName = executionProfileName; + return this; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/DefaultFluentGraphStatement.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/DefaultFluentGraphStatement.java new file mode 100644 index 00000000000..0f6f1faabbf --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/DefaultFluentGraphStatement.java @@ -0,0 +1,106 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph; + +import com.datastax.dse.driver.api.core.graph.FluentGraphStatement; +import com.datastax.oss.driver.api.core.ConsistencyLevel; +import com.datastax.oss.driver.api.core.config.DriverExecutionProfile; +import com.datastax.oss.driver.api.core.metadata.Node; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.nio.ByteBuffer; +import java.time.Duration; +import java.util.Map; +import net.jcip.annotations.Immutable; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; + +@Immutable +public class DefaultFluentGraphStatement extends GraphStatementBase + implements FluentGraphStatement { + + private final GraphTraversal traversal; + + public DefaultFluentGraphStatement( + GraphTraversal traversal, + Boolean isIdempotent, + Duration timeout, + Node node, + long timestamp, + DriverExecutionProfile executionProfile, + String executionProfileName, + Map customPayload, + String graphName, + String traversalSource, + String subProtocol, + ConsistencyLevel consistencyLevel, + ConsistencyLevel readConsistencyLevel, + ConsistencyLevel writeConsistencyLevel) { + super( + isIdempotent, + timeout, + node, + timestamp, + executionProfile, + executionProfileName, + customPayload, + graphName, + traversalSource, + subProtocol, + consistencyLevel, + readConsistencyLevel, + writeConsistencyLevel); + this.traversal = traversal; + } + + @Override + protected FluentGraphStatement newInstance( + Boolean isIdempotent, + Duration timeout, + Node node, + long timestamp, + DriverExecutionProfile executionProfile, + String executionProfileName, + Map customPayload, + String graphName, + String traversalSource, + String subProtocol, + ConsistencyLevel consistencyLevel, + ConsistencyLevel readConsistencyLevel, + ConsistencyLevel writeConsistencyLevel) { + return new DefaultFluentGraphStatement( + traversal, + isIdempotent, + timeout, + node, + timestamp, + executionProfile, + executionProfileName, + customPayload, + graphName, + traversalSource, + subProtocol, + consistencyLevel, + readConsistencyLevel, + writeConsistencyLevel); + } + + @NonNull + @Override + public GraphTraversal getTraversal() { + return traversal; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/DefaultScriptGraphStatement.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/DefaultScriptGraphStatement.java new file mode 100644 index 00000000000..71f79134237 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/DefaultScriptGraphStatement.java @@ -0,0 +1,207 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph; + +import com.datastax.dse.driver.api.core.graph.ScriptGraphStatement; +import com.datastax.oss.driver.api.core.ConsistencyLevel; +import com.datastax.oss.driver.api.core.config.DriverExecutionProfile; +import com.datastax.oss.driver.api.core.metadata.Node; +import com.datastax.oss.protocol.internal.util.collection.NullAllowingImmutableMap; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.nio.ByteBuffer; +import java.time.Duration; +import java.util.Map; +import net.jcip.annotations.Immutable; + +@Immutable +public class DefaultScriptGraphStatement extends GraphStatementBase + implements ScriptGraphStatement { + + private final String script; + private final Boolean isSystemQuery; + private final NullAllowingImmutableMap queryParams; + + public DefaultScriptGraphStatement( + String script, + Map queryParams, + Boolean isSystemQuery, + Boolean isIdempotent, + Duration timeout, + Node node, + long timestamp, + DriverExecutionProfile executionProfile, + String executionProfileName, + Map customPayload, + String graphName, + String traversalSource, + String subProtocol, + ConsistencyLevel consistencyLevel, + ConsistencyLevel readConsistencyLevel, + ConsistencyLevel writeConsistencyLevel) { + super( + isIdempotent, + timeout, + node, + timestamp, + executionProfile, + executionProfileName, + customPayload, + graphName, + traversalSource, + subProtocol, + consistencyLevel, + readConsistencyLevel, + writeConsistencyLevel); + this.script = script; + this.isSystemQuery = isSystemQuery; + this.queryParams = NullAllowingImmutableMap.copyOf(queryParams); + } + + //// Script GraphStatement level options + + @NonNull + @Override + public String getScript() { + return script; + } + + @NonNull + @Override + public ScriptGraphStatement setSystemQuery(@Nullable Boolean newValue) { + return new DefaultScriptGraphStatement( + script, + queryParams, + newValue, + isIdempotent(), + getTimeout(), + getNode(), + getTimestamp(), + getExecutionProfile(), + getExecutionProfileName(), + getCustomPayload(), + getGraphName(), + getTraversalSource(), + getSubProtocol(), + getConsistencyLevel(), + getReadConsistencyLevel(), + getWriteConsistencyLevel()); + } + + @Nullable + @Override + public Boolean isSystemQuery() { + return isSystemQuery; + } + + @NonNull + @Override + public Map getQueryParams() { + return this.queryParams; + } + + @NonNull + @Override + public ScriptGraphStatement setQueryParam(@NonNull String name, @Nullable Object value) { + NullAllowingImmutableMap.Builder newQueryParamsBuilder = + NullAllowingImmutableMap.builder(); + for (Map.Entry entry : queryParams.entrySet()) { + if (!entry.getKey().equals(name)) { + newQueryParamsBuilder.put(entry.getKey(), entry.getValue()); + } + } + newQueryParamsBuilder.put(name, value); + return setQueryParams(newQueryParamsBuilder.build()); + } + + @NonNull + @Override + public ScriptGraphStatement removeQueryParam(@NonNull String name) { + if (!queryParams.containsKey(name)) { + return this; + } else { + NullAllowingImmutableMap.Builder newQueryParamsBuilder = + NullAllowingImmutableMap.builder(); + for (Map.Entry entry : queryParams.entrySet()) { + if (!entry.getKey().equals(name)) { + newQueryParamsBuilder.put(entry.getKey(), entry.getValue()); + } + } + return setQueryParams(newQueryParamsBuilder.build()); + } + } + + private ScriptGraphStatement setQueryParams(Map newQueryParams) { + return new DefaultScriptGraphStatement( + script, + newQueryParams, + isSystemQuery, + isIdempotent(), + getTimeout(), + getNode(), + getTimestamp(), + getExecutionProfile(), + getExecutionProfileName(), + getCustomPayload(), + getGraphName(), + getTraversalSource(), + getSubProtocol(), + getConsistencyLevel(), + getReadConsistencyLevel(), + getWriteConsistencyLevel()); + } + + @Override + protected ScriptGraphStatement newInstance( + Boolean isIdempotent, + Duration timeout, + Node node, + long timestamp, + DriverExecutionProfile executionProfile, + String executionProfileName, + Map customPayload, + String graphName, + String traversalSource, + String subProtocol, + ConsistencyLevel consistencyLevel, + ConsistencyLevel readConsistencyLevel, + ConsistencyLevel writeConsistencyLevel) { + return new DefaultScriptGraphStatement( + script, + queryParams, + isSystemQuery, + isIdempotent, + timeout, + node, + timestamp, + executionProfile, + executionProfileName, + customPayload, + graphName, + traversalSource, + subProtocol, + consistencyLevel, + readConsistencyLevel, + writeConsistencyLevel); + } + + @Override + public String toString() { + return String.format("ScriptGraphStatement['%s', params: %s]", this.script, this.queryParams); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/DseGraphRemoteConnection.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/DseGraphRemoteConnection.java new file mode 100644 index 00000000000..a5ec0a1d115 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/DseGraphRemoteConnection.java @@ -0,0 +1,54 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph; + +import com.datastax.oss.driver.api.core.CqlSession; +import com.datastax.oss.driver.api.core.config.DriverExecutionProfile; +import java.util.concurrent.CompletableFuture; +import net.jcip.annotations.Immutable; +import org.apache.tinkerpop.gremlin.process.remote.RemoteConnection; +import org.apache.tinkerpop.gremlin.process.remote.traversal.RemoteTraversal; +import org.apache.tinkerpop.gremlin.process.traversal.Bytecode; + +@Immutable +public class DseGraphRemoteConnection implements RemoteConnection { + + private final CqlSession session; + private final DriverExecutionProfile executionProfile; + private final String executionProfileName; + + public DseGraphRemoteConnection( + CqlSession session, DriverExecutionProfile executionProfile, String executionProfileName) { + this.session = session; + this.executionProfile = executionProfile; + this.executionProfileName = executionProfileName; + } + + @Override + public CompletableFuture> submitAsync(Bytecode bytecode) { + return session + .executeAsync(new BytecodeGraphStatement(bytecode, executionProfile, executionProfileName)) + .toCompletableFuture() + .thenApply(DseGraphTraversal::new); + } + + @Override + public void close() throws Exception { + // do not close the DseSession here. + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/DseGraphTraversal.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/DseGraphTraversal.java new file mode 100644 index 00000000000..e0a5cf2d675 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/DseGraphTraversal.java @@ -0,0 +1,63 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph; + +import com.datastax.dse.driver.api.core.graph.AsyncGraphResultSet; +import com.datastax.dse.driver.api.core.graph.GraphNode; +import java.util.Iterator; +import java.util.NoSuchElementException; +import net.jcip.annotations.NotThreadSafe; +import org.apache.tinkerpop.gremlin.process.remote.traversal.AbstractRemoteTraversal; +import org.apache.tinkerpop.gremlin.process.remote.traversal.DefaultRemoteTraverser; +import org.apache.tinkerpop.gremlin.process.traversal.Traverser; + +@NotThreadSafe +class DseGraphTraversal extends AbstractRemoteTraversal { + + private final Iterator graphNodeIterator; + + public DseGraphTraversal(AsyncGraphResultSet firstPage) { + this.graphNodeIterator = GraphResultSets.toSync(firstPage).iterator(); + } + + @Override + public boolean hasNext() { + return graphNodeIterator.hasNext(); + } + + @Override + public E next() { + return nextTraverser().get(); + } + + @Override + @SuppressWarnings("unchecked") + public Traverser.Admin nextTraverser() { + if (hasNext()) { + GraphNode nextGraphNode = graphNodeIterator.next(); + + // get the Raw object from the ObjectGraphNode, create a new remote Traverser + // with bulk = 1 because bulk is not supported yet. Casting should be ok + // because we have been able to deserialize into the right type. + return new DefaultRemoteTraverser<>((E) nextGraphNode.as(Object.class), 1); + } else { + // finished iterating/nothing to iterate. Normal behaviour. + throw new NoSuchElementException(); + } + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/DsePredicate.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/DsePredicate.java new file mode 100644 index 00000000000..b5f8c30fd8c --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/DsePredicate.java @@ -0,0 +1,35 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph; + +import com.datastax.oss.driver.shaded.guava.common.base.Preconditions; +import java.util.function.BiPredicate; + +/** + * An extension of TinkerPop's {@link BiPredicate} adding simple pre-condition checking methods that + * have to be written in the implementations. + */ +public interface DsePredicate extends BiPredicate { + + default void preEvaluate(Object condition) { + Preconditions.checkArgument( + this.isValidCondition(condition), "Invalid condition provided: %s", condition); + } + + boolean isValidCondition(Object condition); +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/EditDistance.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/EditDistance.java new file mode 100644 index 00000000000..5ab836babbf --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/EditDistance.java @@ -0,0 +1,74 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph; + +import com.datastax.oss.driver.shaded.guava.common.base.Objects; +import com.datastax.oss.driver.shaded.guava.common.base.Preconditions; +import java.io.Serializable; +import net.jcip.annotations.Immutable; + +/** + * A container for a term and maximum edit distance. + * + *

The context in which this is used determines the semantics of the edit distance. For instance, + * it might indicate single-character edits if used with fuzzy search queries or whole word + * movements if used with phrase proximity queries. + */ +@Immutable +public class EditDistance implements Serializable { + + private static final long serialVersionUID = 1L; + + public static final int DEFAULT_EDIT_DISTANCE = 0; + + public final String query; + public final int distance; + + public EditDistance(String query) { + this(query, DEFAULT_EDIT_DISTANCE); + } + + public EditDistance(String query, int distance) { + Preconditions.checkNotNull(query, "Query cannot be null."); + Preconditions.checkArgument(distance >= 0, "Edit distance cannot be negative."); + this.query = query; + this.distance = distance; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof EditDistance)) { + return false; + } + EditDistance that = (EditDistance) o; + return distance == that.distance && Objects.equal(query, that.query); + } + + @Override + public int hashCode() { + return Objects.hashCode(query, distance); + } + + @Override + public String toString() { + return "EditDistance{" + "query='" + query + '\'' + ", distance=" + distance + '}'; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GeoPredicate.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GeoPredicate.java new file mode 100644 index 00000000000..39949e97198 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GeoPredicate.java @@ -0,0 +1,111 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph; + +import com.datastax.dse.driver.api.core.data.geometry.Geometry; +import com.datastax.dse.driver.api.core.data.geometry.LineString; +import com.datastax.dse.driver.api.core.data.geometry.Point; +import com.datastax.dse.driver.api.core.data.geometry.Polygon; +import com.datastax.dse.driver.api.core.graph.predicates.Geo; +import com.datastax.dse.driver.internal.core.data.geometry.Distance; +import com.datastax.oss.driver.shaded.guava.common.base.Preconditions; + +/** + * List of predicates for geolocation usage with DseGraph and Search indexes. Should not be accessed + * directly but through the {@link Geo} static methods. + */ +public enum GeoPredicate implements DsePredicate { + + /** Matches values within the distance specified by the condition over a Haversine geometry. */ + inside { + @Override + public boolean test(Object value, Object condition) { + preEvaluate(condition); + if (value == null) { + return false; + } + Preconditions.checkArgument(value instanceof Geometry); + Distance distance = (Distance) condition; + if (value instanceof Point) { + return haversineDistanceInDegrees(distance.getCenter(), (Point) value) + <= distance.getRadius(); + } else if (value instanceof Polygon) { + for (Point point : ((Polygon) value).getExteriorRing()) { + if (haversineDistanceInDegrees(distance.getCenter(), point) > distance.getRadius()) { + return false; + } + } + } else if (value instanceof LineString) { + for (Point point : ((LineString) value).getPoints()) { + if (haversineDistanceInDegrees(distance.getCenter(), point) > distance.getRadius()) { + return false; + } + } + } else { + throw new UnsupportedOperationException( + String.format("Value type '%s' unsupported", value.getClass().getName())); + } + + return true; + } + + @Override + public String toString() { + return "inside"; + } + }, + + /** + * Matches values contained in the geometric entity specified by the condition on a 2D Euclidean + * plane. + */ + insideCartesian { + @Override + public boolean test(Object value, Object condition) { + preEvaluate(condition); + if (value == null) { + return false; + } + Preconditions.checkArgument(value instanceof Geometry); + return ((Geometry) condition).contains((Geometry) value); + } + + @Override + public String toString() { + return "insideCartesian"; + } + }; + + @Override + public boolean isValidCondition(Object condition) { + return condition != null; + } + + static double haversineDistanceInDegrees(Point p1, Point p2) { + double dLat = Math.toRadians(p2.Y() - p1.Y()); + double dLon = Math.toRadians(p2.X() - p1.X()); + double lat1 = Math.toRadians(p1.Y()); + double lat2 = Math.toRadians(p2.Y()); + + double a = + Math.pow(Math.sin(dLat / 2), 2) + + Math.pow(Math.sin(dLon / 2), 2) * Math.cos(lat1) * Math.cos(lat2); + double c = 2 * Math.asin(Math.sqrt(a)); + return Math.toDegrees(c); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GeoUtils.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GeoUtils.java new file mode 100644 index 00000000000..80d55dac69d --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GeoUtils.java @@ -0,0 +1,27 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph; + +public class GeoUtils { + private static final double DEGREES_TO_RADIANS = Math.PI / 180; + private static final double EARTH_MEAN_RADIUS_KM = 6371.0087714; + private static final double DEG_TO_KM = DEGREES_TO_RADIANS * EARTH_MEAN_RADIUS_KM; + private static final double KM_TO_MILES = 0.621371192; + public static final double KM_TO_DEG = 1 / DEG_TO_KM; + public static final double MILES_TO_KM = 1 / KM_TO_MILES; +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GraphConversions.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GraphConversions.java new file mode 100644 index 00000000000..c95b26b2e26 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GraphConversions.java @@ -0,0 +1,410 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph; + +import static java.nio.charset.StandardCharsets.UTF_8; + +import com.datastax.dse.driver.api.core.config.DseDriverOption; +import com.datastax.dse.driver.api.core.graph.BatchGraphStatement; +import com.datastax.dse.driver.api.core.graph.FluentGraphStatement; +import com.datastax.dse.driver.api.core.graph.GraphNode; +import com.datastax.dse.driver.api.core.graph.GraphStatement; +import com.datastax.dse.driver.api.core.graph.ScriptGraphStatement; +import com.datastax.dse.driver.internal.core.graph.binary.GraphBinaryModule; +import com.datastax.dse.driver.internal.core.graph.binary.buffer.DseNettyBufferFactory; +import com.datastax.dse.protocol.internal.request.RawBytesQuery; +import com.datastax.dse.protocol.internal.request.query.ContinuousPagingOptions; +import com.datastax.dse.protocol.internal.request.query.DseQueryOptions; +import com.datastax.oss.driver.api.core.ConsistencyLevel; +import com.datastax.oss.driver.api.core.DefaultConsistencyLevel; +import com.datastax.oss.driver.api.core.ProtocolVersion; +import com.datastax.oss.driver.api.core.config.DefaultDriverOption; +import com.datastax.oss.driver.api.core.config.DriverExecutionProfile; +import com.datastax.oss.driver.api.core.cql.Statement; +import com.datastax.oss.driver.api.core.type.codec.TypeCodecs; +import com.datastax.oss.driver.internal.core.context.InternalDriverContext; +import com.datastax.oss.driver.internal.core.cql.Conversions; +import com.datastax.oss.driver.shaded.guava.common.annotations.VisibleForTesting; +import com.datastax.oss.driver.shaded.guava.common.base.Preconditions; +import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableList; +import com.datastax.oss.driver.shaded.guava.common.collect.Iterators; +import com.datastax.oss.protocol.internal.Message; +import com.datastax.oss.protocol.internal.ProtocolConstants; +import com.datastax.oss.protocol.internal.request.Query; +import com.datastax.oss.protocol.internal.util.collection.NullAllowingImmutableMap; +import io.netty.buffer.ByteBuf; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.ByteBuffer; +import java.time.Duration; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import org.apache.tinkerpop.gremlin.process.traversal.Traverser; +import org.apache.tinkerpop.gremlin.structure.io.Buffer; +import org.apache.tinkerpop.gremlin.structure.io.BufferFactory; + +/** + * Utility class to move boilerplate out of {@link GraphRequestHandler}. + * + *

We extend {@link Conversions} only for methods that can be directly reused as-is; if something + * needs to be customized, it will be duplicated here instead of making the parent method + * "pluggable". + */ +public class GraphConversions extends Conversions { + + static final String GRAPH_LANG_OPTION_KEY = "graph-language"; + static final String GRAPH_NAME_OPTION_KEY = "graph-name"; + static final String GRAPH_SOURCE_OPTION_KEY = "graph-source"; + static final String GRAPH_READ_CONSISTENCY_LEVEL_OPTION_KEY = "graph-read-consistency"; + static final String GRAPH_WRITE_CONSISTENCY_LEVEL_OPTION_KEY = "graph-write-consistency"; + static final String GRAPH_RESULTS_OPTION_KEY = "graph-results"; + static final String GRAPH_TIMEOUT_OPTION_KEY = "request-timeout"; + static final String GRAPH_BINARY_QUERY_OPTION_KEY = "graph-binary-query"; + + static final String LANGUAGE_GROOVY = "gremlin-groovy"; + static final String LANGUAGE_BYTECODE = "bytecode-json"; + + private static final BufferFactory FACTORY = new DseNettyBufferFactory(); + + @VisibleForTesting static final byte[] EMPTY_STRING_QUERY = "".getBytes(UTF_8); + + public static Message createContinuousMessageFromGraphStatement( + GraphStatement statement, + GraphProtocol subProtocol, + DriverExecutionProfile config, + InternalDriverContext context, + GraphBinaryModule graphBinaryModule) { + + final List encodedQueryParams; + if (!(statement instanceof ScriptGraphStatement) + || ((ScriptGraphStatement) statement).getQueryParams().isEmpty()) { + encodedQueryParams = Collections.emptyList(); + } else { + try { + Map queryParams = ((ScriptGraphStatement) statement).getQueryParams(); + if (subProtocol.isGraphBinary()) { + Buffer graphBinaryParams = graphBinaryModule.serialize(queryParams); + encodedQueryParams = Collections.singletonList(graphBinaryParams.nioBuffer()); + graphBinaryParams.release(); + } else { + encodedQueryParams = + Collections.singletonList( + GraphSONUtils.serializeToByteBuffer(queryParams, subProtocol)); + } + } catch (IOException e) { + throw new UncheckedIOException( + "Couldn't serialize parameters for GraphStatement: " + statement, e); + } + } + + int consistencyLevel = + DefaultConsistencyLevel.valueOf(config.getString(DefaultDriverOption.REQUEST_CONSISTENCY)) + .getProtocolCode(); + + long timestamp = statement.getTimestamp(); + if (timestamp == Statement.NO_DEFAULT_TIMESTAMP) { + timestamp = context.getTimestampGenerator().next(); + } + + int pageSize = config.getInt(DseDriverOption.GRAPH_CONTINUOUS_PAGING_PAGE_SIZE); + int maxPages = config.getInt(DseDriverOption.GRAPH_CONTINUOUS_PAGING_MAX_PAGES); + int maxPagesPerSecond = + config.getInt(DseDriverOption.GRAPH_CONTINUOUS_PAGING_MAX_PAGES_PER_SECOND); + int maxEnqueuedPages = + config.getInt(DseDriverOption.GRAPH_CONTINUOUS_PAGING_MAX_ENQUEUED_PAGES); + ContinuousPagingOptions options = + new ContinuousPagingOptions(maxPages, maxPagesPerSecond, maxEnqueuedPages); + + DseQueryOptions queryOptions = + new DseQueryOptions( + consistencyLevel, + encodedQueryParams, + Collections.emptyMap(), // ignored by the DSE Graph server + true, // also ignored + pageSize, + null, + ProtocolConstants.ConsistencyLevel.LOCAL_SERIAL, // also ignored + timestamp, + null, // also ignored + false, // graph CP does not support sizeInBytes + options); + + if (statement instanceof ScriptGraphStatement) { + return new Query(((ScriptGraphStatement) statement).getScript(), queryOptions); + } else { + return new RawBytesQuery(getQueryBytes(statement, subProtocol), queryOptions); + } + } + + static Message createMessageFromGraphStatement( + GraphStatement statement, + GraphProtocol subProtocol, + DriverExecutionProfile config, + InternalDriverContext context, + GraphBinaryModule graphBinaryModule) { + + final List encodedQueryParams; + if (!(statement instanceof ScriptGraphStatement) + || ((ScriptGraphStatement) statement).getQueryParams().isEmpty()) { + encodedQueryParams = Collections.emptyList(); + } else { + try { + Map queryParams = ((ScriptGraphStatement) statement).getQueryParams(); + if (subProtocol.isGraphBinary()) { + Buffer graphBinaryParams = graphBinaryModule.serialize(queryParams); + encodedQueryParams = Collections.singletonList(graphBinaryParams.nioBuffer()); + graphBinaryParams.release(); + } else { + encodedQueryParams = + Collections.singletonList( + GraphSONUtils.serializeToByteBuffer(queryParams, subProtocol)); + } + } catch (IOException e) { + throw new UncheckedIOException( + "Couldn't serialize parameters for GraphStatement: " + statement, e); + } + } + + ConsistencyLevel consistency = statement.getConsistencyLevel(); + int consistencyLevel = + (consistency == null) + ? context + .getConsistencyLevelRegistry() + .nameToCode(config.getString(DefaultDriverOption.REQUEST_CONSISTENCY)) + : consistency.getProtocolCode(); + + long timestamp = statement.getTimestamp(); + if (timestamp == Statement.NO_DEFAULT_TIMESTAMP) { + timestamp = context.getTimestampGenerator().next(); + } + + DseQueryOptions queryOptions = + new DseQueryOptions( + consistencyLevel, + encodedQueryParams, + Collections.emptyMap(), // ignored by the DSE Graph server + true, // also ignored + 50, // also ignored + null, // also ignored + ProtocolConstants.ConsistencyLevel.LOCAL_SERIAL, // also ignored + timestamp, + null, // also ignored + false, // also ignored + null // also ignored + ); + + if (statement instanceof ScriptGraphStatement) { + return new Query(((ScriptGraphStatement) statement).getScript(), queryOptions); + } else { + return new RawBytesQuery(getQueryBytes(statement, subProtocol), queryOptions); + } + } + + // This method returns either a Bytecode object, or a List if the statement is a + // BatchGraphStatement + @VisibleForTesting + public static Object bytecodeToSerialize(GraphStatement statement) { + Preconditions.checkArgument( + statement instanceof FluentGraphStatement + || statement instanceof BatchGraphStatement + || statement instanceof BytecodeGraphStatement, + "To serialize bytecode the query must be a fluent or batch statement, but was: %s", + statement.getClass()); + + Object toSerialize; + if (statement instanceof FluentGraphStatement) { + toSerialize = ((FluentGraphStatement) statement).getTraversal().asAdmin().getBytecode(); + } else if (statement instanceof BatchGraphStatement) { + // transform the Iterator to List + toSerialize = + ImmutableList.copyOf( + Iterators.transform( + ((BatchGraphStatement) statement).iterator(), + traversal -> traversal.asAdmin().getBytecode())); + } else { + toSerialize = ((BytecodeGraphStatement) statement).getBytecode(); + } + return toSerialize; + } + + private static byte[] getQueryBytes(GraphStatement statement, GraphProtocol graphSubProtocol) { + try { + return graphSubProtocol.isGraphBinary() + // if GraphBinary, the query is encoded in the custom payload, and not in the query field + // see GraphConversions#createCustomPayload() + ? EMPTY_STRING_QUERY + : GraphSONUtils.serializeToBytes(bytecodeToSerialize(statement), graphSubProtocol); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + public static Map createCustomPayload( + GraphStatement statement, + GraphProtocol subProtocol, + DriverExecutionProfile config, + InternalDriverContext context, + GraphBinaryModule graphBinaryModule) { + + ProtocolVersion protocolVersion = context.getProtocolVersion(); + + NullAllowingImmutableMap.Builder payload = + NullAllowingImmutableMap.builder(); + Map statementOptions = statement.getCustomPayload(); + payload.putAll(statementOptions); + + final String graphLanguage; + + // Don't override anything that's already provided at the statement level + if (!statementOptions.containsKey(GRAPH_LANG_OPTION_KEY)) { + graphLanguage = + statement instanceof ScriptGraphStatement ? LANGUAGE_GROOVY : LANGUAGE_BYTECODE; + payload.put(GRAPH_LANG_OPTION_KEY, TypeCodecs.TEXT.encode(graphLanguage, protocolVersion)); + } else { + graphLanguage = + TypeCodecs.TEXT.decode(statementOptions.get(GRAPH_LANG_OPTION_KEY), protocolVersion); + Preconditions.checkNotNull( + graphLanguage, "A null value was set for the graph-language custom payload key."); + } + + if (!isSystemQuery(statement, config)) { + if (!statementOptions.containsKey(GRAPH_NAME_OPTION_KEY)) { + String graphName = statement.getGraphName(); + if (graphName == null) { + graphName = config.getString(DseDriverOption.GRAPH_NAME, null); + } + if (graphName != null) { + payload.put(GRAPH_NAME_OPTION_KEY, TypeCodecs.TEXT.encode(graphName, protocolVersion)); + } + } + if (!statementOptions.containsKey(GRAPH_SOURCE_OPTION_KEY)) { + String traversalSource = statement.getTraversalSource(); + if (traversalSource == null) { + traversalSource = config.getString(DseDriverOption.GRAPH_TRAVERSAL_SOURCE, null); + } + if (traversalSource != null) { + payload.put( + GRAPH_SOURCE_OPTION_KEY, TypeCodecs.TEXT.encode(traversalSource, protocolVersion)); + } + } + } + + // the payload allows null entry values so doing a get directly here and checking for null + final ByteBuffer payloadInitialProtocol = statementOptions.get(GRAPH_RESULTS_OPTION_KEY); + if (payloadInitialProtocol == null) { + Preconditions.checkNotNull(subProtocol); + payload.put( + GRAPH_RESULTS_OPTION_KEY, + TypeCodecs.TEXT.encode(subProtocol.toInternalCode(), protocolVersion)); + } else { + subProtocol = + GraphProtocol.fromString(TypeCodecs.TEXT.decode(payloadInitialProtocol, protocolVersion)); + } + + if (subProtocol.isGraphBinary() && graphLanguage.equals(LANGUAGE_BYTECODE)) { + Object bytecodeQuery = bytecodeToSerialize(statement); + try { + Buffer bytecodeByteBuf = graphBinaryModule.serialize(bytecodeQuery); + payload.put(GRAPH_BINARY_QUERY_OPTION_KEY, bytecodeByteBuf.nioBuffer()); + bytecodeByteBuf.release(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + if (!statementOptions.containsKey(GRAPH_READ_CONSISTENCY_LEVEL_OPTION_KEY)) { + ConsistencyLevel readCl = statement.getReadConsistencyLevel(); + String readClString = + readCl != null + ? readCl.name() + : config.getString(DseDriverOption.GRAPH_READ_CONSISTENCY_LEVEL, null); + if (readClString != null) { + payload.put( + GRAPH_READ_CONSISTENCY_LEVEL_OPTION_KEY, + TypeCodecs.TEXT.encode(readClString, protocolVersion)); + } + } + + if (!statementOptions.containsKey(GRAPH_WRITE_CONSISTENCY_LEVEL_OPTION_KEY)) { + ConsistencyLevel writeCl = statement.getWriteConsistencyLevel(); + String writeClString = + writeCl != null + ? writeCl.name() + : config.getString(DseDriverOption.GRAPH_WRITE_CONSISTENCY_LEVEL, null); + if (writeClString != null) { + payload.put( + GRAPH_WRITE_CONSISTENCY_LEVEL_OPTION_KEY, + TypeCodecs.TEXT.encode(writeClString, protocolVersion)); + } + } + + if (!statementOptions.containsKey(GRAPH_TIMEOUT_OPTION_KEY)) { + Duration timeout = statement.getTimeout(); + if (timeout == null) { + timeout = config.getDuration(DseDriverOption.GRAPH_TIMEOUT, Duration.ZERO); + } + if (timeout != null && !timeout.isZero()) { + payload.put( + GRAPH_TIMEOUT_OPTION_KEY, + TypeCodecs.BIGINT.encode(timeout.toMillis(), protocolVersion)); + } + } + return payload.build(); + } + + private static boolean isSystemQuery(GraphStatement statement, DriverExecutionProfile config) { + if (statement instanceof ScriptGraphStatement) { + Boolean statementValue = ((ScriptGraphStatement) statement).isSystemQuery(); + if (statementValue != null) { + return statementValue; + } + } + return config.getBoolean(DseDriverOption.GRAPH_IS_SYSTEM_QUERY, false); + } + + public static GraphNode createGraphBinaryGraphNode( + List data, GraphBinaryModule graphBinaryModule) throws IOException { + // there should be only one column in the given row + Preconditions.checkArgument(data.size() == 1, "Invalid row given to deserialize"); + + Buffer toDeserialize = FACTORY.wrap(data.get(0)); + Object deserializedObject = graphBinaryModule.deserialize(toDeserialize); + toDeserialize.release(); + assert deserializedObject instanceof Traverser + : "Graph protocol error. Received object should be a Traverser but it is not."; + return new ObjectGraphNode(deserializedObject); + } + + public static Duration resolveGraphRequestTimeout( + GraphStatement statement, InternalDriverContext context) { + DriverExecutionProfile executionProfile = resolveExecutionProfile(statement, context); + return statement.getTimeout() != null + ? statement.getTimeout() + : executionProfile.getDuration(DseDriverOption.GRAPH_TIMEOUT); + } + + public static GraphProtocol resolveGraphSubProtocol( + GraphStatement statement, + GraphSupportChecker graphSupportChecker, + InternalDriverContext context) { + DriverExecutionProfile executionProfile = + Conversions.resolveExecutionProfile(statement, context); + return graphSupportChecker.inferGraphProtocol(statement, executionProfile, context); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GraphExecutionInfoConverter.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GraphExecutionInfoConverter.java new file mode 100644 index 00000000000..b6472f690d3 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GraphExecutionInfoConverter.java @@ -0,0 +1,180 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph; + +import com.datastax.dse.driver.api.core.graph.GraphStatement; +import com.datastax.oss.driver.api.core.cql.ExecutionInfo; +import com.datastax.oss.driver.api.core.cql.QueryTrace; +import com.datastax.oss.driver.api.core.cql.Statement; +import com.datastax.oss.driver.api.core.metadata.Node; +import com.datastax.oss.driver.api.core.session.Request; +import com.datastax.oss.driver.internal.core.util.concurrent.CompletableFutures; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.nio.ByteBuffer; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.UUID; +import java.util.concurrent.CompletionStage; + +/** + * Handles conversions from / to GraphExecutionInfo and ExecutionInfo since GraphExecutionInfo has + * been deprecated by JAVA-2556. + */ +public class GraphExecutionInfoConverter { + + /** + * Called exclusively from default methods in API interfaces {@link + * com.datastax.dse.driver.api.core.graph.GraphResultSet} and {@link + * com.datastax.dse.driver.api.core.graph.AsyncGraphResultSet}. Graph result set implementations + * do not use this method but rather the other one below. + */ + @SuppressWarnings("deprecation") + public static ExecutionInfo convert( + com.datastax.dse.driver.api.core.graph.GraphExecutionInfo graphExecutionInfo) { + return new ExecutionInfo() { + + @NonNull + @Override + public Request getRequest() { + return graphExecutionInfo.getStatement(); + } + + @NonNull + @Override + public Statement getStatement() { + throw new ClassCastException("GraphStatement cannot be cast to Statement"); + } + + @Nullable + @Override + public Node getCoordinator() { + return graphExecutionInfo.getCoordinator(); + } + + @Override + public int getSpeculativeExecutionCount() { + return graphExecutionInfo.getSpeculativeExecutionCount(); + } + + @Override + public int getSuccessfulExecutionIndex() { + return graphExecutionInfo.getSuccessfulExecutionIndex(); + } + + @NonNull + @Override + public List> getErrors() { + return graphExecutionInfo.getErrors(); + } + + @Nullable + @Override + public ByteBuffer getPagingState() { + return null; + } + + @NonNull + @Override + public List getWarnings() { + return graphExecutionInfo.getWarnings(); + } + + @NonNull + @Override + public Map getIncomingPayload() { + return graphExecutionInfo.getIncomingPayload(); + } + + @Override + public boolean isSchemaInAgreement() { + return true; + } + + @Nullable + @Override + public UUID getTracingId() { + return null; + } + + @NonNull + @Override + public CompletionStage getQueryTraceAsync() { + return CompletableFutures.failedFuture( + new IllegalStateException("Tracing was disabled for this request")); + } + + @Override + public int getResponseSizeInBytes() { + return -1; + } + + @Override + public int getCompressedResponseSizeInBytes() { + return -1; + } + }; + } + + /** + * Called from graph result set implementations, to convert the original {@link ExecutionInfo} + * produced by request handlers into the (deprecated) type GraphExecutionInfo. + */ + @SuppressWarnings("deprecation") + public static com.datastax.dse.driver.api.core.graph.GraphExecutionInfo convert( + ExecutionInfo executionInfo) { + return new com.datastax.dse.driver.api.core.graph.GraphExecutionInfo() { + + @Override + public GraphStatement getStatement() { + return (GraphStatement) executionInfo.getRequest(); + } + + @Override + public Node getCoordinator() { + return executionInfo.getCoordinator(); + } + + @Override + public int getSpeculativeExecutionCount() { + return executionInfo.getSpeculativeExecutionCount(); + } + + @Override + public int getSuccessfulExecutionIndex() { + return executionInfo.getSuccessfulExecutionIndex(); + } + + @Override + public List> getErrors() { + return executionInfo.getErrors(); + } + + @Override + public List getWarnings() { + return executionInfo.getWarnings(); + } + + @Override + public Map getIncomingPayload() { + return executionInfo.getIncomingPayload(); + } + }; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GraphProtocol.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GraphProtocol.java new file mode 100644 index 00000000000..6b7a9f4c430 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GraphProtocol.java @@ -0,0 +1,73 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph; + +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public enum GraphProtocol { + GRAPHSON_1_0("graphson-1.0"), + GRAPHSON_2_0("graphson-2.0"), + GRAPH_BINARY_1_0("graph-binary-1.0"), + ; + + private static final Map BY_CODE; + + static { + Map tmp = new HashMap<>(); + for (GraphProtocol value : values()) { + tmp.put(value.stringRepresentation, value); + } + BY_CODE = Collections.unmodifiableMap(tmp); + } + + private final String stringRepresentation; + + GraphProtocol(String stringRepresentation) { + this.stringRepresentation = stringRepresentation; + } + + @NonNull + public String toInternalCode() { + return stringRepresentation; + } + + @NonNull + public static GraphProtocol fromString(@Nullable String stringRepresentation) { + if (stringRepresentation == null || !BY_CODE.containsKey(stringRepresentation)) { + StringBuilder sb = + new StringBuilder( + String.format( + "Graph protocol used [\"%s\"] unknown. Possible values are: [ \"%s\"", + stringRepresentation, GraphProtocol.values()[0].toInternalCode())); + for (int i = 1; i < GraphProtocol.values().length; i++) { + sb.append(String.format(", \"%s\"", GraphProtocol.values()[i].toInternalCode())); + } + sb.append("]"); + throw new IllegalArgumentException(sb.toString()); + } + return BY_CODE.get(stringRepresentation); + } + + public boolean isGraphBinary() { + return this == GRAPH_BINARY_1_0; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GraphRequestAsyncProcessor.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GraphRequestAsyncProcessor.java new file mode 100644 index 00000000000..050b03c66f4 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GraphRequestAsyncProcessor.java @@ -0,0 +1,104 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph; + +import com.datastax.dse.driver.api.core.graph.AsyncGraphResultSet; +import com.datastax.dse.driver.api.core.graph.BatchGraphStatement; +import com.datastax.dse.driver.api.core.graph.FluentGraphStatement; +import com.datastax.dse.driver.api.core.graph.GraphStatement; +import com.datastax.dse.driver.api.core.graph.ScriptGraphStatement; +import com.datastax.dse.driver.internal.core.graph.binary.GraphBinaryModule; +import com.datastax.oss.driver.api.core.session.Request; +import com.datastax.oss.driver.api.core.type.reflect.GenericType; +import com.datastax.oss.driver.internal.core.context.DefaultDriverContext; +import com.datastax.oss.driver.internal.core.context.InternalDriverContext; +import com.datastax.oss.driver.internal.core.session.DefaultSession; +import com.datastax.oss.driver.internal.core.session.RequestProcessor; +import com.datastax.oss.driver.internal.core.util.concurrent.CompletableFutures; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.concurrent.CompletionStage; +import net.jcip.annotations.ThreadSafe; +import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryReader; +import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryWriter; +import org.apache.tinkerpop.gremlin.structure.io.binary.TypeSerializerRegistry; + +@ThreadSafe +public class GraphRequestAsyncProcessor + implements RequestProcessor, CompletionStage> { + + private final GraphBinaryModule graphBinaryModule; + private final GraphSupportChecker graphSupportChecker; + + public GraphRequestAsyncProcessor( + DefaultDriverContext context, GraphSupportChecker graphSupportChecker) { + TypeSerializerRegistry typeSerializerRegistry = + GraphBinaryModule.createDseTypeSerializerRegistry(context); + this.graphBinaryModule = + new GraphBinaryModule( + new GraphBinaryReader(typeSerializerRegistry), + new GraphBinaryWriter(typeSerializerRegistry)); + this.graphSupportChecker = graphSupportChecker; + } + + @NonNull + public GraphBinaryModule getGraphBinaryModule() { + return graphBinaryModule; + } + + @Override + public boolean canProcess(Request request, GenericType resultType) { + return (request instanceof ScriptGraphStatement + || request instanceof FluentGraphStatement + || request instanceof BatchGraphStatement + || request instanceof BytecodeGraphStatement) + && resultType.equals(GraphStatement.ASYNC); + } + + @Override + public CompletionStage process( + GraphStatement request, + DefaultSession session, + InternalDriverContext context, + String sessionLogPrefix) { + + if (graphSupportChecker.isPagingEnabled(request, context)) { + return new ContinuousGraphRequestHandler( + request, + session, + context, + sessionLogPrefix, + getGraphBinaryModule(), + graphSupportChecker) + .handle(); + } else { + return new GraphRequestHandler( + request, + session, + context, + sessionLogPrefix, + getGraphBinaryModule(), + graphSupportChecker) + .handle(); + } + } + + @Override + public CompletionStage newFailure(RuntimeException error) { + return CompletableFutures.failedFuture(error); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GraphRequestHandler.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GraphRequestHandler.java new file mode 100644 index 00000000000..5c9ceb00df2 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GraphRequestHandler.java @@ -0,0 +1,871 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph; + +import com.datastax.dse.driver.api.core.graph.AsyncGraphResultSet; +import com.datastax.dse.driver.api.core.graph.GraphNode; +import com.datastax.dse.driver.api.core.graph.GraphStatement; +import com.datastax.dse.driver.api.core.metrics.DseNodeMetric; +import com.datastax.dse.driver.api.core.metrics.DseSessionMetric; +import com.datastax.dse.driver.internal.core.graph.binary.GraphBinaryModule; +import com.datastax.oss.driver.api.core.AllNodesFailedException; +import com.datastax.oss.driver.api.core.DriverException; +import com.datastax.oss.driver.api.core.DriverTimeoutException; +import com.datastax.oss.driver.api.core.NodeUnavailableException; +import com.datastax.oss.driver.api.core.RequestThrottlingException; +import com.datastax.oss.driver.api.core.config.DefaultDriverOption; +import com.datastax.oss.driver.api.core.config.DriverExecutionProfile; +import com.datastax.oss.driver.api.core.connection.FrameTooLongException; +import com.datastax.oss.driver.api.core.cql.ExecutionInfo; +import com.datastax.oss.driver.api.core.metadata.Node; +import com.datastax.oss.driver.api.core.metrics.DefaultNodeMetric; +import com.datastax.oss.driver.api.core.metrics.DefaultSessionMetric; +import com.datastax.oss.driver.api.core.retry.RetryPolicy; +import com.datastax.oss.driver.api.core.retry.RetryVerdict; +import com.datastax.oss.driver.api.core.servererrors.BootstrappingException; +import com.datastax.oss.driver.api.core.servererrors.CoordinatorException; +import com.datastax.oss.driver.api.core.servererrors.FunctionFailureException; +import com.datastax.oss.driver.api.core.servererrors.ProtocolError; +import com.datastax.oss.driver.api.core.servererrors.QueryValidationException; +import com.datastax.oss.driver.api.core.servererrors.ReadTimeoutException; +import com.datastax.oss.driver.api.core.servererrors.UnavailableException; +import com.datastax.oss.driver.api.core.servererrors.WriteTimeoutException; +import com.datastax.oss.driver.api.core.session.throttling.RequestThrottler; +import com.datastax.oss.driver.api.core.session.throttling.Throttled; +import com.datastax.oss.driver.api.core.tracker.RequestTracker; +import com.datastax.oss.driver.internal.core.channel.DriverChannel; +import com.datastax.oss.driver.internal.core.channel.ResponseCallback; +import com.datastax.oss.driver.internal.core.context.InternalDriverContext; +import com.datastax.oss.driver.internal.core.cql.Conversions; +import com.datastax.oss.driver.internal.core.cql.DefaultExecutionInfo; +import com.datastax.oss.driver.internal.core.metadata.DefaultNode; +import com.datastax.oss.driver.internal.core.metrics.NodeMetricUpdater; +import com.datastax.oss.driver.internal.core.metrics.SessionMetricUpdater; +import com.datastax.oss.driver.internal.core.session.DefaultSession; +import com.datastax.oss.driver.internal.core.tracker.NoopRequestTracker; +import com.datastax.oss.driver.internal.core.tracker.RequestLogger; +import com.datastax.oss.driver.internal.core.util.Loggers; +import com.datastax.oss.driver.internal.core.util.collection.SimpleQueryPlan; +import com.datastax.oss.protocol.internal.Frame; +import com.datastax.oss.protocol.internal.Message; +import com.datastax.oss.protocol.internal.response.Error; +import com.datastax.oss.protocol.internal.response.Result; +import com.datastax.oss.protocol.internal.response.result.Rows; +import com.datastax.oss.protocol.internal.response.result.Void; +import edu.umd.cs.findbugs.annotations.NonNull; +import io.netty.handler.codec.EncoderException; +import io.netty.util.Timeout; +import io.netty.util.Timer; +import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.GenericFutureListener; +import java.nio.ByteBuffer; +import java.time.Duration; +import java.util.AbstractMap; +import java.util.ArrayDeque; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import net.jcip.annotations.ThreadSafe; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@ThreadSafe +public class GraphRequestHandler implements Throttled { + + private static final Logger LOG = LoggerFactory.getLogger(GraphRequestHandler.class); + + private static final long NANOTIME_NOT_MEASURED_YET = -1; + private static final int NO_SUCCESSFUL_EXECUTION = -1; + + private final long startTimeNanos; + private final String logPrefix; + private final GraphStatement initialStatement; + private final DefaultSession session; + private final InternalDriverContext context; + protected final CompletableFuture result; + private final Timer timer; + + /** + * How many speculative executions are currently running (including the initial execution). We + * track this in order to know when to fail the request if all executions have reached the end of + * the query plan. + */ + private final AtomicInteger activeExecutionsCount; + + /** + * How many speculative executions have started (excluding the initial execution), whether they + * have completed or not. We track this in order to fill {@link + * ExecutionInfo#getSpeculativeExecutionCount()}. + */ + private final AtomicInteger startedSpeculativeExecutionsCount; + + private final Timeout scheduledTimeout; + private final List scheduledExecutions; + private final List inFlightCallbacks; + private final RequestThrottler throttler; + private final RequestTracker requestTracker; + private final SessionMetricUpdater sessionMetricUpdater; + private final GraphBinaryModule graphBinaryModule; + private final GraphSupportChecker graphSupportChecker; + + // The errors on the nodes that were already tried (lazily initialized on the first error). + // We don't use a map because nodes can appear multiple times. + private volatile List> errors; + + GraphRequestHandler( + @NonNull GraphStatement statement, + @NonNull DefaultSession dseSession, + @NonNull InternalDriverContext context, + @NonNull String sessionLogPrefix, + @NonNull GraphBinaryModule graphBinaryModule, + @NonNull GraphSupportChecker graphSupportChecker) { + this.startTimeNanos = System.nanoTime(); + this.logPrefix = sessionLogPrefix + "|" + this.hashCode(); + LOG.trace("[{}] Creating new Graph request handler for request {}", logPrefix, statement); + this.initialStatement = statement; + this.session = dseSession; + this.context = context; + this.graphSupportChecker = graphSupportChecker; + this.result = new CompletableFuture<>(); + this.result.exceptionally( + t -> { + try { + if (t instanceof CancellationException) { + cancelScheduledTasks(); + context.getRequestThrottler().signalCancel(this); + } + } catch (Throwable t2) { + Loggers.warnWithException(LOG, "[{}] Uncaught exception", logPrefix, t2); + } + return null; + }); + this.graphBinaryModule = graphBinaryModule; + this.timer = context.getNettyOptions().getTimer(); + + this.activeExecutionsCount = new AtomicInteger(1); + this.startedSpeculativeExecutionsCount = new AtomicInteger(0); + this.scheduledExecutions = new CopyOnWriteArrayList<>(); + this.inFlightCallbacks = new CopyOnWriteArrayList<>(); + + this.requestTracker = context.getRequestTracker(); + this.sessionMetricUpdater = session.getMetricUpdater(); + + Duration timeout = GraphConversions.resolveGraphRequestTimeout(statement, context); + this.scheduledTimeout = scheduleTimeout(timeout); + + this.throttler = context.getRequestThrottler(); + this.throttler.register(this); + } + + @Override + public void onThrottleReady(boolean wasDelayed) { + DriverExecutionProfile executionProfile = + Conversions.resolveExecutionProfile(initialStatement, context); + if (wasDelayed + // avoid call to nanoTime() if metric is disabled: + && sessionMetricUpdater.isEnabled( + DefaultSessionMetric.THROTTLING_DELAY, executionProfile.getName())) { + sessionMetricUpdater.updateTimer( + DefaultSessionMetric.THROTTLING_DELAY, + executionProfile.getName(), + System.nanoTime() - startTimeNanos, + TimeUnit.NANOSECONDS); + } + Queue queryPlan = + initialStatement.getNode() != null + ? new SimpleQueryPlan(initialStatement.getNode()) + : context + .getLoadBalancingPolicyWrapper() + .newQueryPlan(initialStatement, executionProfile.getName(), session); + sendRequest(initialStatement, null, queryPlan, 0, 0, true); + } + + public CompletionStage handle() { + return result; + } + + private Timeout scheduleTimeout(Duration timeoutDuration) { + if (timeoutDuration != null && timeoutDuration.toNanos() > 0) { + try { + return this.timer.newTimeout( + (Timeout timeout1) -> + setFinalError( + initialStatement, + new DriverTimeoutException("Query timed out after " + timeoutDuration), + null, + NO_SUCCESSFUL_EXECUTION), + timeoutDuration.toNanos(), + TimeUnit.NANOSECONDS); + } catch (IllegalStateException e) { + // If we raced with session shutdown the timer might be closed already, rethrow with a more + // explicit message + result.completeExceptionally( + "cannot be started once stopped".equals(e.getMessage()) + ? new IllegalStateException("Session is closed") + : e); + } + } + return null; + } + + /** + * Sends the request to the next available node. + * + * @param retriedNode if not null, it will be attempted first before the rest of the query plan. + * @param queryPlan the list of nodes to try (shared with all other executions) + * @param currentExecutionIndex 0 for the initial execution, 1 for the first speculative one, etc. + * @param retryCount the number of times that the retry policy was invoked for this execution + * already (note that some internal retries don't go through the policy, and therefore don't + * increment this counter) + * @param scheduleNextExecution whether to schedule the next speculative execution + */ + private void sendRequest( + GraphStatement statement, + Node retriedNode, + Queue queryPlan, + int currentExecutionIndex, + int retryCount, + boolean scheduleNextExecution) { + if (result.isDone()) { + return; + } + Node node = retriedNode; + DriverChannel channel = null; + if (node == null || (channel = session.getChannel(node, logPrefix)) == null) { + while (!result.isDone() && (node = queryPlan.poll()) != null) { + channel = session.getChannel(node, logPrefix); + if (channel != null) { + break; + } else { + recordError(node, new NodeUnavailableException(node)); + } + } + } + if (channel == null) { + // We've reached the end of the query plan without finding any node to write to + if (!result.isDone() && activeExecutionsCount.decrementAndGet() == 0) { + // We're the last execution so fail the result + setFinalError( + statement, + AllNodesFailedException.fromErrors(this.errors), + null, + NO_SUCCESSFUL_EXECUTION); + } + } else { + NodeResponseCallback nodeResponseCallback = + new NodeResponseCallback( + statement, + node, + queryPlan, + channel, + currentExecutionIndex, + retryCount, + scheduleNextExecution, + logPrefix); + DriverExecutionProfile executionProfile = + Conversions.resolveExecutionProfile(statement, context); + GraphProtocol graphSubProtocol = + GraphConversions.resolveGraphSubProtocol(statement, graphSupportChecker, context); + Message message = + GraphConversions.createMessageFromGraphStatement( + statement, graphSubProtocol, executionProfile, context, graphBinaryModule); + Map customPayload = + GraphConversions.createCustomPayload( + statement, graphSubProtocol, executionProfile, context, graphBinaryModule); + channel + .write(message, statement.isTracing(), customPayload, nodeResponseCallback) + .addListener(nodeResponseCallback); + } + } + + private void recordError(Node node, Throwable error) { + // Use a local variable to do only a single single volatile read in the nominal case + List> errorsSnapshot = this.errors; + if (errorsSnapshot == null) { + synchronized (GraphRequestHandler.this) { + errorsSnapshot = this.errors; + if (errorsSnapshot == null) { + this.errors = errorsSnapshot = new CopyOnWriteArrayList<>(); + } + } + } + errorsSnapshot.add(new AbstractMap.SimpleEntry<>(node, error)); + } + + private void cancelScheduledTasks() { + if (this.scheduledTimeout != null) { + this.scheduledTimeout.cancel(); + } + if (scheduledExecutions != null) { + for (Timeout scheduledExecution : scheduledExecutions) { + scheduledExecution.cancel(); + } + } + for (NodeResponseCallback callback : inFlightCallbacks) { + callback.cancel(); + } + } + + private void setFinalResult( + Result resultMessage, Frame responseFrame, NodeResponseCallback callback) { + try { + ExecutionInfo executionInfo = buildExecutionInfo(callback, responseFrame); + DriverExecutionProfile executionProfile = + Conversions.resolveExecutionProfile(callback.statement, context); + GraphProtocol subProtocol = + GraphConversions.resolveGraphSubProtocol( + callback.statement, graphSupportChecker, context); + Queue graphNodes = new ArrayDeque<>(); + for (List row : ((Rows) resultMessage).getData()) { + if (subProtocol.isGraphBinary()) { + graphNodes.offer( + GraphConversions.createGraphBinaryGraphNode( + row, GraphRequestHandler.this.graphBinaryModule)); + } else { + graphNodes.offer(GraphSONUtils.createGraphNode(row, subProtocol)); + } + } + + DefaultAsyncGraphResultSet resultSet = + new DefaultAsyncGraphResultSet(executionInfo, graphNodes, subProtocol); + if (result.complete(resultSet)) { + cancelScheduledTasks(); + throttler.signalSuccess(this); + + // Only call nanoTime() if we're actually going to use it + long completionTimeNanos = NANOTIME_NOT_MEASURED_YET, + totalLatencyNanos = NANOTIME_NOT_MEASURED_YET; + if (!(requestTracker instanceof NoopRequestTracker)) { + completionTimeNanos = System.nanoTime(); + totalLatencyNanos = completionTimeNanos - startTimeNanos; + long nodeLatencyNanos = completionTimeNanos - callback.nodeStartTimeNanos; + requestTracker.onNodeSuccess( + callback.statement, nodeLatencyNanos, executionProfile, callback.node, logPrefix); + requestTracker.onSuccess( + callback.statement, totalLatencyNanos, executionProfile, callback.node, logPrefix); + } + if (sessionMetricUpdater.isEnabled( + DseSessionMetric.GRAPH_REQUESTS, executionProfile.getName())) { + if (completionTimeNanos == NANOTIME_NOT_MEASURED_YET) { + completionTimeNanos = System.nanoTime(); + totalLatencyNanos = completionTimeNanos - startTimeNanos; + } + sessionMetricUpdater.updateTimer( + DseSessionMetric.GRAPH_REQUESTS, + executionProfile.getName(), + totalLatencyNanos, + TimeUnit.NANOSECONDS); + } + } + // log the warnings if they have NOT been disabled + if (!executionInfo.getWarnings().isEmpty() + && executionProfile.getBoolean(DefaultDriverOption.REQUEST_LOG_WARNINGS) + && LOG.isWarnEnabled()) { + logServerWarnings(callback.statement, executionInfo.getWarnings()); + } + } catch (Throwable error) { + setFinalError(callback.statement, error, callback.node, NO_SUCCESSFUL_EXECUTION); + } + } + + private void logServerWarnings(GraphStatement statement, List warnings) { + DriverExecutionProfile executionProfile = + Conversions.resolveExecutionProfile(statement, context); + // use the RequestLogFormatter to format the query + StringBuilder statementString = new StringBuilder(); + context + .getRequestLogFormatter() + .appendRequest( + statement, + executionProfile.getInt( + DefaultDriverOption.REQUEST_LOGGER_MAX_QUERY_LENGTH, + RequestLogger.DEFAULT_REQUEST_LOGGER_MAX_QUERY_LENGTH), + executionProfile.getBoolean( + DefaultDriverOption.REQUEST_LOGGER_VALUES, + RequestLogger.DEFAULT_REQUEST_LOGGER_SHOW_VALUES), + executionProfile.getInt( + DefaultDriverOption.REQUEST_LOGGER_MAX_VALUES, + RequestLogger.DEFAULT_REQUEST_LOGGER_MAX_VALUES), + executionProfile.getInt( + DefaultDriverOption.REQUEST_LOGGER_MAX_VALUE_LENGTH, + RequestLogger.DEFAULT_REQUEST_LOGGER_MAX_VALUE_LENGTH), + statementString); + // log each warning separately + warnings.forEach( + (warning) -> + LOG.warn("Query '{}' generated server side warning(s): {}", statementString, warning)); + } + + private ExecutionInfo buildExecutionInfo(NodeResponseCallback callback, Frame responseFrame) { + DriverExecutionProfile executionProfile = + Conversions.resolveExecutionProfile(callback.statement, context); + return new DefaultExecutionInfo( + callback.statement, + callback.node, + startedSpeculativeExecutionsCount.get(), + callback.execution, + errors, + null, + responseFrame, + true, + session, + context, + executionProfile); + } + + @Override + public void onThrottleFailure(@NonNull RequestThrottlingException error) { + DriverExecutionProfile executionProfile = + Conversions.resolveExecutionProfile(initialStatement, context); + sessionMetricUpdater.incrementCounter( + DefaultSessionMetric.THROTTLING_ERRORS, executionProfile.getName()); + setFinalError(initialStatement, error, null, NO_SUCCESSFUL_EXECUTION); + } + + private void setFinalError( + GraphStatement statement, Throwable error, Node node, int execution) { + DriverExecutionProfile executionProfile = + Conversions.resolveExecutionProfile(statement, context); + if (error instanceof DriverException) { + ((DriverException) error) + .setExecutionInfo( + new DefaultExecutionInfo( + statement, + node, + startedSpeculativeExecutionsCount.get(), + execution, + errors, + null, + null, + true, + session, + context, + executionProfile)); + } + if (result.completeExceptionally(error)) { + cancelScheduledTasks(); + if (!(requestTracker instanceof NoopRequestTracker)) { + long latencyNanos = System.nanoTime() - startTimeNanos; + requestTracker.onError(statement, error, latencyNanos, executionProfile, node, logPrefix); + } + if (error instanceof DriverTimeoutException) { + throttler.signalTimeout(this); + sessionMetricUpdater.incrementCounter( + DseSessionMetric.GRAPH_CLIENT_TIMEOUTS, executionProfile.getName()); + } else if (!(error instanceof RequestThrottlingException)) { + throttler.signalError(this, error); + } + } + } + + /** + * Handles the interaction with a single node in the query plan. + * + *

An instance of this class is created each time we (re)try a node. + */ + private class NodeResponseCallback + implements ResponseCallback, GenericFutureListener> { + + private final long nodeStartTimeNanos = System.nanoTime(); + private final GraphStatement statement; + private final Node node; + private final Queue queryPlan; + private final DriverChannel channel; + // The identifier of the current execution (0 for the initial execution, 1 for the first + // speculative execution, etc.) + private final int execution; + // How many times we've invoked the retry policy and it has returned a "retry" decision (0 for + // the first attempt of each execution). + private final int retryCount; + private final boolean scheduleNextExecution; + private final String logPrefix; + private final DriverExecutionProfile executionProfile; + + private NodeResponseCallback( + GraphStatement statement, + Node node, + Queue queryPlan, + DriverChannel channel, + int execution, + int retryCount, + boolean scheduleNextExecution, + String logPrefix) { + this.statement = statement; + this.node = node; + this.queryPlan = queryPlan; + this.channel = channel; + this.execution = execution; + this.retryCount = retryCount; + this.scheduleNextExecution = scheduleNextExecution; + this.logPrefix = logPrefix + "|" + execution; + this.executionProfile = Conversions.resolveExecutionProfile(statement, context); + } + + // this gets invoked once the write completes. + @Override + public void operationComplete(Future future) { + if (!future.isSuccess()) { + Throwable error = future.cause(); + if (error instanceof EncoderException + && error.getCause() instanceof FrameTooLongException) { + trackNodeError(node, error.getCause(), NANOTIME_NOT_MEASURED_YET); + setFinalError(statement, error.getCause(), node, execution); + } else { + LOG.trace( + "[{}] Failed to send request on {}, trying next node (cause: {})", + logPrefix, + channel, + error); + recordError(node, error); + trackNodeError(node, error, NANOTIME_NOT_MEASURED_YET); + ((DefaultNode) node) + .getMetricUpdater() + .incrementCounter(DefaultNodeMetric.UNSENT_REQUESTS, executionProfile.getName()); + sendRequest( + statement, + null, + queryPlan, + execution, + retryCount, + scheduleNextExecution); // try next node + } + } else { + LOG.trace("[{}] Request sent on {}", logPrefix, channel); + if (result.isDone()) { + // If the handler completed since the last time we checked, cancel directly because we + // don't know if cancelScheduledTasks() has run yet + cancel(); + } else { + inFlightCallbacks.add(this); + if (scheduleNextExecution + && Conversions.resolveIdempotence(statement, executionProfile)) { + int nextExecution = execution + 1; + long nextDelay; + try { + nextDelay = + Conversions.resolveSpeculativeExecutionPolicy(context, executionProfile) + .nextExecution(node, null, statement, nextExecution); + } catch (Throwable cause) { + // This is a bug in the policy, but not fatal since we have at least one other + // execution already running. Don't fail the whole request. + LOG.error( + "[{}] Unexpected error while invoking the speculative execution policy", + logPrefix, + cause); + return; + } + if (nextDelay >= 0) { + scheduleSpeculativeExecution(nextExecution, nextDelay); + } else { + LOG.trace( + "[{}] Speculative execution policy returned {}, no next execution", + logPrefix, + nextDelay); + } + } + } + } + } + + private void scheduleSpeculativeExecution(int index, long delay) { + LOG.trace("[{}] Scheduling speculative execution {} in {} ms", logPrefix, index, delay); + try { + scheduledExecutions.add( + timer.newTimeout( + (Timeout timeout1) -> { + if (!result.isDone()) { + LOG.trace( + "[{}] Starting speculative execution {}", + GraphRequestHandler.this.logPrefix, + index); + activeExecutionsCount.incrementAndGet(); + startedSpeculativeExecutionsCount.incrementAndGet(); + // Note that `node` is the first node of the execution, it might not be the + // "slow" one if there were retries, but in practice retries are rare. + ((DefaultNode) node) + .getMetricUpdater() + .incrementCounter( + DefaultNodeMetric.SPECULATIVE_EXECUTIONS, executionProfile.getName()); + sendRequest(statement, null, queryPlan, index, 0, true); + } + }, + delay, + TimeUnit.MILLISECONDS)); + } catch (IllegalStateException e) { + // If we're racing with session shutdown, the timer might be stopped already. We don't want + // to schedule more executions anyway, so swallow the error. + if (!"cannot be started once stopped".equals(e.getMessage())) { + Loggers.warnWithException( + LOG, "[{}] Error while scheduling speculative execution", logPrefix, e); + } + } + } + + @Override + public void onResponse(Frame responseFrame) { + long nodeResponseTimeNanos = NANOTIME_NOT_MEASURED_YET; + NodeMetricUpdater nodeMetricUpdater = ((DefaultNode) node).getMetricUpdater(); + if (nodeMetricUpdater.isEnabled(DseNodeMetric.GRAPH_MESSAGES, executionProfile.getName())) { + nodeResponseTimeNanos = System.nanoTime(); + long nodeLatency = System.nanoTime() - nodeStartTimeNanos; + nodeMetricUpdater.updateTimer( + DseNodeMetric.GRAPH_MESSAGES, + executionProfile.getName(), + nodeLatency, + TimeUnit.NANOSECONDS); + } + inFlightCallbacks.remove(this); + if (result.isDone()) { + return; + } + try { + Message responseMessage = responseFrame.message; + if (responseMessage instanceof Result) { + LOG.trace("[{}] Got result, completing", logPrefix); + setFinalResult((Result) responseMessage, responseFrame, this); + } else if (responseMessage instanceof Error) { + LOG.trace("[{}] Got error response, processing", logPrefix); + processErrorResponse((Error) responseMessage); + } else { + trackNodeError( + node, + new IllegalStateException("Unexpected response " + responseMessage), + nodeResponseTimeNanos); + setFinalError( + statement, + new IllegalStateException("Unexpected response " + responseMessage), + node, + execution); + } + } catch (Throwable t) { + trackNodeError(node, t, nodeResponseTimeNanos); + setFinalError(statement, t, node, execution); + } + } + + private void processErrorResponse(Error errorMessage) { + CoordinatorException error = Conversions.toThrowable(node, errorMessage, context); + NodeMetricUpdater metricUpdater = ((DefaultNode) node).getMetricUpdater(); + if (error instanceof BootstrappingException) { + LOG.trace("[{}] {} is bootstrapping, trying next node", logPrefix, node); + recordError(node, error); + trackNodeError(node, error, NANOTIME_NOT_MEASURED_YET); + sendRequest(statement, null, queryPlan, execution, retryCount, false); + } else if (error instanceof QueryValidationException + || error instanceof FunctionFailureException + || error instanceof ProtocolError) { + LOG.trace("[{}] Unrecoverable error, rethrowing", logPrefix); + metricUpdater.incrementCounter(DefaultNodeMetric.OTHER_ERRORS, executionProfile.getName()); + trackNodeError(node, error, NANOTIME_NOT_MEASURED_YET); + setFinalError(statement, error, node, execution); + } else { + RetryPolicy retryPolicy = Conversions.resolveRetryPolicy(context, executionProfile); + RetryVerdict verdict; + if (error instanceof ReadTimeoutException) { + ReadTimeoutException readTimeout = (ReadTimeoutException) error; + verdict = + retryPolicy.onReadTimeoutVerdict( + statement, + readTimeout.getConsistencyLevel(), + readTimeout.getBlockFor(), + readTimeout.getReceived(), + readTimeout.wasDataPresent(), + retryCount); + updateErrorMetrics( + metricUpdater, + verdict, + DefaultNodeMetric.READ_TIMEOUTS, + DefaultNodeMetric.RETRIES_ON_READ_TIMEOUT, + DefaultNodeMetric.IGNORES_ON_READ_TIMEOUT); + } else if (error instanceof WriteTimeoutException) { + WriteTimeoutException writeTimeout = (WriteTimeoutException) error; + verdict = + Conversions.resolveIdempotence(statement, executionProfile) + ? retryPolicy.onWriteTimeoutVerdict( + statement, + writeTimeout.getConsistencyLevel(), + writeTimeout.getWriteType(), + writeTimeout.getBlockFor(), + writeTimeout.getReceived(), + retryCount) + : RetryVerdict.RETHROW; + updateErrorMetrics( + metricUpdater, + verdict, + DefaultNodeMetric.WRITE_TIMEOUTS, + DefaultNodeMetric.RETRIES_ON_WRITE_TIMEOUT, + DefaultNodeMetric.IGNORES_ON_WRITE_TIMEOUT); + } else if (error instanceof UnavailableException) { + UnavailableException unavailable = (UnavailableException) error; + verdict = + retryPolicy.onUnavailableVerdict( + statement, + unavailable.getConsistencyLevel(), + unavailable.getRequired(), + unavailable.getAlive(), + retryCount); + updateErrorMetrics( + metricUpdater, + verdict, + DefaultNodeMetric.UNAVAILABLES, + DefaultNodeMetric.RETRIES_ON_UNAVAILABLE, + DefaultNodeMetric.IGNORES_ON_UNAVAILABLE); + } else { + verdict = + Conversions.resolveIdempotence(statement, executionProfile) + ? retryPolicy.onErrorResponseVerdict(statement, error, retryCount) + : RetryVerdict.RETHROW; + updateErrorMetrics( + metricUpdater, + verdict, + DefaultNodeMetric.OTHER_ERRORS, + DefaultNodeMetric.RETRIES_ON_OTHER_ERROR, + DefaultNodeMetric.IGNORES_ON_OTHER_ERROR); + } + processRetryVerdict(verdict, error); + } + } + + private void processRetryVerdict(RetryVerdict verdict, Throwable error) { + LOG.trace("[{}] Processing retry decision {}", logPrefix, verdict); + switch (verdict.getRetryDecision()) { + case RETRY_SAME: + recordError(node, error); + trackNodeError(node, error, NANOTIME_NOT_MEASURED_YET); + sendRequest( + verdict.getRetryRequest(statement), + node, + queryPlan, + execution, + retryCount + 1, + false); + break; + case RETRY_NEXT: + recordError(node, error); + trackNodeError(node, error, NANOTIME_NOT_MEASURED_YET); + sendRequest( + verdict.getRetryRequest(statement), + null, + queryPlan, + execution, + retryCount + 1, + false); + break; + case RETHROW: + trackNodeError(node, error, NANOTIME_NOT_MEASURED_YET); + setFinalError(statement, error, node, execution); + break; + case IGNORE: + setFinalResult(Void.INSTANCE, null, this); + break; + } + } + + private void updateErrorMetrics( + NodeMetricUpdater metricUpdater, + RetryVerdict verdict, + DefaultNodeMetric error, + DefaultNodeMetric retriesOnError, + DefaultNodeMetric ignoresOnError) { + metricUpdater.incrementCounter(error, executionProfile.getName()); + switch (verdict.getRetryDecision()) { + case RETRY_SAME: + case RETRY_NEXT: + metricUpdater.incrementCounter(DefaultNodeMetric.RETRIES, executionProfile.getName()); + metricUpdater.incrementCounter(retriesOnError, executionProfile.getName()); + break; + case IGNORE: + metricUpdater.incrementCounter(DefaultNodeMetric.IGNORES, executionProfile.getName()); + metricUpdater.incrementCounter(ignoresOnError, executionProfile.getName()); + break; + case RETHROW: + // nothing do do + } + } + + @Override + public void onFailure(Throwable error) { + inFlightCallbacks.remove(this); + if (result.isDone()) { + return; + } + LOG.trace("[{}] Request failure, processing: {}", logPrefix, error); + RetryVerdict verdict; + if (!Conversions.resolveIdempotence(statement, executionProfile) + || error instanceof FrameTooLongException) { + verdict = RetryVerdict.RETHROW; + } else { + try { + RetryPolicy retryPolicy = Conversions.resolveRetryPolicy(context, executionProfile); + verdict = retryPolicy.onRequestAbortedVerdict(statement, error, retryCount); + } catch (Throwable cause) { + setFinalError( + statement, + new IllegalStateException("Unexpected error while invoking the retry policy", cause), + node, + NO_SUCCESSFUL_EXECUTION); + return; + } + } + processRetryVerdict(verdict, error); + updateErrorMetrics( + ((DefaultNode) node).getMetricUpdater(), + verdict, + DefaultNodeMetric.ABORTED_REQUESTS, + DefaultNodeMetric.RETRIES_ON_ABORTED, + DefaultNodeMetric.IGNORES_ON_ABORTED); + } + + void cancel() { + try { + if (!channel.closeFuture().isDone()) { + this.channel.cancel(this); + } + } catch (Throwable t) { + Loggers.warnWithException(LOG, "[{}] Error cancelling", logPrefix, t); + } + } + + /** + * @param nodeResponseTimeNanos the time we received the response, if it's already been + * measured. If {@link #NANOTIME_NOT_MEASURED_YET}, it hasn't and we need to measure it now + * (this is to avoid unnecessary calls to System.nanoTime) + */ + private void trackNodeError(Node node, Throwable error, long nodeResponseTimeNanos) { + if (requestTracker instanceof NoopRequestTracker) { + return; + } + if (nodeResponseTimeNanos == NANOTIME_NOT_MEASURED_YET) { + nodeResponseTimeNanos = System.nanoTime(); + } + long latencyNanos = nodeResponseTimeNanos - this.nodeStartTimeNanos; + requestTracker.onNodeError(statement, error, latencyNanos, executionProfile, node, logPrefix); + } + + @Override + public String toString() { + return logPrefix; + } + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GraphRequestSyncProcessor.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GraphRequestSyncProcessor.java new file mode 100644 index 00000000000..bc2381482a8 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GraphRequestSyncProcessor.java @@ -0,0 +1,71 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph; + +import com.datastax.dse.driver.api.core.graph.AsyncGraphResultSet; +import com.datastax.dse.driver.api.core.graph.BatchGraphStatement; +import com.datastax.dse.driver.api.core.graph.FluentGraphStatement; +import com.datastax.dse.driver.api.core.graph.GraphResultSet; +import com.datastax.dse.driver.api.core.graph.GraphStatement; +import com.datastax.dse.driver.api.core.graph.ScriptGraphStatement; +import com.datastax.oss.driver.api.core.session.Request; +import com.datastax.oss.driver.api.core.type.reflect.GenericType; +import com.datastax.oss.driver.internal.core.context.InternalDriverContext; +import com.datastax.oss.driver.internal.core.session.DefaultSession; +import com.datastax.oss.driver.internal.core.session.RequestProcessor; +import com.datastax.oss.driver.internal.core.util.concurrent.BlockingOperation; +import com.datastax.oss.driver.internal.core.util.concurrent.CompletableFutures; +import net.jcip.annotations.ThreadSafe; + +@ThreadSafe +public class GraphRequestSyncProcessor + implements RequestProcessor, GraphResultSet> { + + private final GraphRequestAsyncProcessor asyncProcessor; + + public GraphRequestSyncProcessor(GraphRequestAsyncProcessor asyncProcessor) { + this.asyncProcessor = asyncProcessor; + } + + @Override + public boolean canProcess(Request request, GenericType resultType) { + return (request instanceof ScriptGraphStatement + || request instanceof FluentGraphStatement + || request instanceof BatchGraphStatement + || request instanceof BytecodeGraphStatement) + && resultType.equals(GraphStatement.SYNC); + } + + @Override + public GraphResultSet process( + GraphStatement request, + DefaultSession session, + InternalDriverContext context, + String sessionLogPrefix) { + BlockingOperation.checkNotDriverThread(); + AsyncGraphResultSet firstPage = + CompletableFutures.getUninterruptibly( + asyncProcessor.process(request, session, context, sessionLogPrefix)); + return GraphResultSets.toSync(firstPage); + } + + @Override + public GraphResultSet newFailure(RuntimeException error) { + throw error; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GraphResultIterator.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GraphResultIterator.java new file mode 100644 index 00000000000..7e9043affec --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GraphResultIterator.java @@ -0,0 +1,79 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph; + +import com.datastax.dse.driver.api.core.graph.GraphNode; +import com.datastax.oss.driver.internal.core.util.CountingIterator; +import com.datastax.oss.driver.shaded.guava.common.base.Preconditions; +import java.util.Queue; +import net.jcip.annotations.NotThreadSafe; +import org.apache.tinkerpop.gremlin.process.traversal.Traverser; + +@NotThreadSafe // wraps a mutable queue +class GraphResultIterator extends CountingIterator { + + private final Queue data; + private final GraphProtocol graphProtocol; + + // Sometimes a traversal can yield the same result multiple times consecutively. To avoid + // duplicating the data, DSE graph sends it only once with a counter indicating how many times + // it's repeated. + private long repeat = 0; + private GraphNode lastGraphNode = null; + + GraphResultIterator(Queue data, GraphProtocol graphProtocol) { + super(data.size()); + this.data = data; + this.graphProtocol = graphProtocol; + } + + @Override + protected GraphNode computeNext() { + if (repeat > 1) { + repeat -= 1; + // Note that we don't make a defensive copy, we assume the client won't mutate the node + return lastGraphNode; + } + + GraphNode container = data.poll(); + if (container == null) { + return endOfData(); + } + + if (graphProtocol.isGraphBinary()) { + // results are contained in a Traverser object and not a Map if the protocol + // is GraphBinary + Preconditions.checkState( + container.as(Object.class) instanceof Traverser, + "Graph protocol error. Received object should be a Traverser but it is not."); + Traverser t = container.as(Traverser.class); + this.repeat = t.bulk(); + this.lastGraphNode = new ObjectGraphNode(t.get()); + return lastGraphNode; + } else { + // The repeat counter is called "bulk" in the JSON payload + GraphNode b = container.getByKey("bulk"); + if (b != null) { + this.repeat = b.asLong(); + } + + lastGraphNode = container.getByKey("result"); + return lastGraphNode; + } + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GraphResultSets.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GraphResultSets.java new file mode 100644 index 00000000000..fb21f857cfa --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GraphResultSets.java @@ -0,0 +1,32 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph; + +import com.datastax.dse.driver.api.core.graph.AsyncGraphResultSet; +import com.datastax.dse.driver.api.core.graph.GraphResultSet; + +public class GraphResultSets { + + public static GraphResultSet toSync(AsyncGraphResultSet firstPage) { + if (firstPage.hasMorePages()) { + return new MultiPageGraphResultSet(firstPage); + } else { + return new SinglePageGraphResultSet(firstPage); + } + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GraphSON1SerdeTP.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GraphSON1SerdeTP.java new file mode 100644 index 00000000000..f880bca3764 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GraphSON1SerdeTP.java @@ -0,0 +1,346 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph; + +import com.datastax.dse.driver.api.core.data.geometry.Geometry; +import com.datastax.dse.driver.api.core.data.geometry.LineString; +import com.datastax.dse.driver.api.core.data.geometry.Point; +import com.datastax.dse.driver.api.core.data.geometry.Polygon; +import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableList; +import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableMap; +import com.datastax.oss.driver.shaded.guava.common.net.InetAddresses; +import java.io.IOException; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.util.List; +import java.util.Map; +import org.apache.tinkerpop.shaded.jackson.core.JsonGenerator; +import org.apache.tinkerpop.shaded.jackson.core.JsonParseException; +import org.apache.tinkerpop.shaded.jackson.core.JsonParser; +import org.apache.tinkerpop.shaded.jackson.core.Version; +import org.apache.tinkerpop.shaded.jackson.databind.DeserializationContext; +import org.apache.tinkerpop.shaded.jackson.databind.JsonDeserializer; +import org.apache.tinkerpop.shaded.jackson.databind.JsonSerializer; +import org.apache.tinkerpop.shaded.jackson.databind.SerializerProvider; +import org.apache.tinkerpop.shaded.jackson.databind.deser.std.StdDeserializer; +import org.apache.tinkerpop.shaded.jackson.databind.module.SimpleModule; +import org.apache.tinkerpop.shaded.jackson.databind.ser.std.StdSerializer; + +public class GraphSON1SerdeTP { + + //////////////////////// DESERIALIZERS //////////////////////// + + /** + * Default deserializer used by the driver for {@link InetAddress} instances. The actual subclass + * returned by this deserializer depends on the type of address: {@link Inet4Address IPV4} or + * {@link Inet6Address IPV6}. + */ + static class DefaultInetAddressDeserializer extends StdDeserializer { + + private static final long serialVersionUID = 1L; + + private final Class inetClass; + + DefaultInetAddressDeserializer(Class inetClass) { + super(inetClass); + this.inetClass = inetClass; + } + + @Override + public T deserialize(JsonParser parser, DeserializationContext ctx) throws IOException { + String ip = parser.readValueAs(String.class); + try { + InetAddress inet = InetAddresses.forString(ip); + return inetClass.cast(inet); + } catch (ClassCastException e) { + throw new JsonParseException( + parser, + String.format("Inet address cannot be cast to %s: %s", inetClass.getSimpleName(), ip), + e); + } catch (IllegalArgumentException e) { + throw new JsonParseException(parser, String.format("Expected inet address, got %s", ip), e); + } + } + } + + /** + * Default deserializer used by the driver for geospatial types. It deserializes such types into + * {@link Geometry} instances. The actual subclass depends on the type being deserialized. + */ + static class DefaultGeometryDeserializer extends StdDeserializer { + + private static final long serialVersionUID = 1L; + + private final Class geometryClass; + + DefaultGeometryDeserializer(Class geometryClass) { + super(geometryClass); + this.geometryClass = geometryClass; + } + + @Override + public T deserialize(JsonParser parser, DeserializationContext ctx) throws IOException { + String wkt = parser.readValueAs(String.class); + Geometry geometry; + if (wkt.startsWith("POINT")) geometry = Point.fromWellKnownText(wkt); + else if (wkt.startsWith("LINESTRING")) geometry = LineString.fromWellKnownText(wkt); + else if (wkt.startsWith("POLYGON")) geometry = Polygon.fromWellKnownText(wkt); + else throw new JsonParseException(parser, "Unknown geometry type: " + wkt); + return geometryClass.cast(geometry); + } + } + + /** Base class for serializing the {@code java.time.*} types to ISO-8061 formats. */ + abstract static class AbstractJavaTimeSerializer extends StdSerializer { + + private static final long serialVersionUID = 1L; + + AbstractJavaTimeSerializer(final Class clazz) { + super(clazz); + } + + @Override + public void serialize( + final T value, final JsonGenerator gen, final SerializerProvider serializerProvider) + throws IOException { + gen.writeString(value.toString()); + } + } + + /** Base class for deserializing the {@code java.time.*} types from ISO-8061 formats. */ + abstract static class AbstractJavaTimeJacksonDeserializer extends StdDeserializer { + + private static final long serialVersionUID = 1L; + + AbstractJavaTimeJacksonDeserializer(final Class clazz) { + super(clazz); + } + + abstract T parse(final String val); + + @Override + public T deserialize( + final JsonParser jsonParser, final DeserializationContext deserializationContext) + throws IOException { + return parse(jsonParser.getText()); + } + } + + static final class DurationJacksonSerializer + extends AbstractJavaTimeSerializer { + + private static final long serialVersionUID = 1L; + + DurationJacksonSerializer() { + super(java.time.Duration.class); + } + } + + static final class DurationJacksonDeserializer + extends AbstractJavaTimeJacksonDeserializer { + + private static final long serialVersionUID = 1L; + + DurationJacksonDeserializer() { + super(java.time.Duration.class); + } + + @Override + public java.time.Duration parse(final String val) { + return java.time.Duration.parse(val); + } + } + + static final class InstantJacksonSerializer + extends AbstractJavaTimeSerializer { + + private static final long serialVersionUID = 1L; + + InstantJacksonSerializer() { + super(java.time.Instant.class); + } + } + + static final class InstantJacksonDeserializer + extends AbstractJavaTimeJacksonDeserializer { + + private static final long serialVersionUID = 1L; + + InstantJacksonDeserializer() { + super(java.time.Instant.class); + } + + @Override + public java.time.Instant parse(final String val) { + return java.time.Instant.parse(val); + } + } + + static final class LocalDateJacksonSerializer + extends AbstractJavaTimeSerializer { + + private static final long serialVersionUID = 1L; + + LocalDateJacksonSerializer() { + super(java.time.LocalDate.class); + } + } + + static final class LocalDateJacksonDeserializer + extends AbstractJavaTimeJacksonDeserializer { + + private static final long serialVersionUID = 1L; + + LocalDateJacksonDeserializer() { + super(java.time.LocalDate.class); + } + + @Override + public java.time.LocalDate parse(final String val) { + return java.time.LocalDate.parse(val); + } + } + + static final class LocalTimeJacksonSerializer + extends AbstractJavaTimeSerializer { + + private static final long serialVersionUID = 1L; + + LocalTimeJacksonSerializer() { + super(java.time.LocalTime.class); + } + } + + static final class LocalTimeJacksonDeserializer + extends AbstractJavaTimeJacksonDeserializer { + + private static final long serialVersionUID = 1L; + + LocalTimeJacksonDeserializer() { + super(java.time.LocalTime.class); + } + + @Override + public java.time.LocalTime parse(final String val) { + return java.time.LocalTime.parse(val); + } + } + + //////////////////////// SERIALIZERS //////////////////////// + + /** Default serializer used by the driver for {@link LegacyGraphNode} instances. */ + static class DefaultGraphNodeSerializer extends StdSerializer { + + private static final long serialVersionUID = 1L; + + DefaultGraphNodeSerializer() { + super(LegacyGraphNode.class); + } + + @Override + public void serialize( + LegacyGraphNode value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) + throws IOException { + jsonGenerator.writeTree(value.getDelegate()); + } + } + + /** + * Default serializer used by the driver for geospatial types. It serializes {@link Geometry} + * instances into their Well-Known Text (WKT) equivalent. + */ + static class DefaultGeometrySerializer extends StdSerializer { + + private static final long serialVersionUID = 1L; + + DefaultGeometrySerializer() { + super(Geometry.class); + } + + @Override + public void serialize( + Geometry value, JsonGenerator jsonGenerator, SerializerProvider serializers) + throws IOException { + jsonGenerator.writeString(value.asWellKnownText()); + } + } + + /** The default Jackson module used by DSE Graph. */ + static class GraphSON1DefaultModule extends SimpleModule { + + private static final long serialVersionUID = 1L; + + GraphSON1DefaultModule(String name, Version version) { + super(name, version, createDeserializers(), createSerializers()); + } + + private static Map, JsonDeserializer> createDeserializers() { + + return ImmutableMap., JsonDeserializer>builder() + + // Inet (there is no built-in deserializer for InetAddress and subclasses) + .put(InetAddress.class, new DefaultInetAddressDeserializer<>(InetAddress.class)) + .put(Inet4Address.class, new DefaultInetAddressDeserializer<>(Inet4Address.class)) + .put(Inet6Address.class, new DefaultInetAddressDeserializer<>(Inet6Address.class)) + + // Geospatial types + .put(Geometry.class, new DefaultGeometryDeserializer<>(Geometry.class)) + .put(Point.class, new DefaultGeometryDeserializer<>(Point.class)) + .put(LineString.class, new DefaultGeometryDeserializer<>(LineString.class)) + .put(Polygon.class, new DefaultGeometryDeserializer<>(Polygon.class)) + .build(); + } + + private static List> createSerializers() { + return ImmutableList.>builder() + .add(new DefaultGraphNodeSerializer()) + .add(new DefaultGeometrySerializer()) + .build(); + } + } + + /** Serializers and deserializers for JSR 310 {@code java.time.*}. */ + static class GraphSON1JavaTimeModule extends SimpleModule { + + private static final long serialVersionUID = 1L; + + GraphSON1JavaTimeModule(String name, Version version) { + super(name, version, createDeserializers(), createSerializers()); + } + + private static Map, JsonDeserializer> createDeserializers() { + + return ImmutableMap., JsonDeserializer>builder() + .put(java.time.Duration.class, new DurationJacksonDeserializer()) + .put(java.time.Instant.class, new InstantJacksonDeserializer()) + .put(java.time.LocalDate.class, new LocalDateJacksonDeserializer()) + .put(java.time.LocalTime.class, new LocalTimeJacksonDeserializer()) + .build(); + } + + private static List> createSerializers() { + return ImmutableList.>builder() + .add(new DurationJacksonSerializer()) + .add(new InstantJacksonSerializer()) + .add(new LocalDateJacksonSerializer()) + .add(new LocalTimeJacksonSerializer()) + .build(); + } + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GraphSON2SerdeTP.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GraphSON2SerdeTP.java new file mode 100644 index 00000000000..d79afc71822 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GraphSON2SerdeTP.java @@ -0,0 +1,430 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph; + +import com.datastax.dse.driver.api.core.data.geometry.Geometry; +import com.datastax.dse.driver.api.core.data.geometry.LineString; +import com.datastax.dse.driver.api.core.data.geometry.Point; +import com.datastax.dse.driver.api.core.data.geometry.Polygon; +import com.datastax.dse.driver.api.core.graph.predicates.Geo; +import com.datastax.dse.driver.api.core.graph.predicates.Search; +import com.datastax.dse.driver.internal.core.data.geometry.DefaultLineString; +import com.datastax.dse.driver.internal.core.data.geometry.DefaultPoint; +import com.datastax.dse.driver.internal.core.data.geometry.DefaultPolygon; +import com.datastax.dse.driver.internal.core.data.geometry.Distance; +import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableMap; +import java.io.IOException; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.tinkerpop.gremlin.process.traversal.P; +import org.apache.tinkerpop.gremlin.process.traversal.util.AndP; +import org.apache.tinkerpop.gremlin.process.traversal.util.ConnectiveP; +import org.apache.tinkerpop.gremlin.process.traversal.util.OrP; +import org.apache.tinkerpop.gremlin.structure.io.graphson.AbstractObjectDeserializer; +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONTokens; +import org.apache.tinkerpop.gremlin.structure.io.graphson.TinkerPopJacksonModule; +import org.apache.tinkerpop.shaded.jackson.core.JsonGenerator; +import org.apache.tinkerpop.shaded.jackson.core.JsonParser; +import org.apache.tinkerpop.shaded.jackson.databind.DeserializationContext; +import org.apache.tinkerpop.shaded.jackson.databind.SerializerProvider; +import org.apache.tinkerpop.shaded.jackson.databind.deser.std.StdDeserializer; +import org.apache.tinkerpop.shaded.jackson.databind.jsontype.TypeSerializer; +import org.apache.tinkerpop.shaded.jackson.databind.module.SimpleModule; +import org.apache.tinkerpop.shaded.jackson.databind.ser.std.StdScalarSerializer; +import org.apache.tinkerpop.shaded.jackson.databind.ser.std.StdSerializer; + +public class GraphSON2SerdeTP { + + /** + * A Jackson Module to use for TinkerPop serialization/deserialization. It extends {@link + * org.apache.tinkerpop.gremlin.structure.io.graphson.TinkerPopJacksonModule} because of the + * specific typing format used in GraphSON. + */ + public static class DseGraphModule extends TinkerPopJacksonModule { + + private static final long serialVersionUID = 1L; + + public DseGraphModule() { + super("dse-driver-2.0"); + addSerializer(DefaultPoint.class, new PointGeometrySerializer()); + addSerializer(DefaultLineString.class, new LineStringGeometrySerializer()); + addSerializer(DefaultPolygon.class, new PolygonGeometrySerializer()); + addSerializer(Distance.class, new DistanceGeometrySerializer()); + // override TinkerPop's P predicates because of DSE's Search and Geo predicates + addSerializer(P.class, new DsePJacksonSerializer()); + addSerializer(EditDistance.class, new EditDistanceSerializer()); + + addDeserializer(DefaultLineString.class, new LineStringGeometryDeserializer()); + addDeserializer(DefaultPoint.class, new PointGeometryDeserializer()); + addDeserializer(DefaultPolygon.class, new PolygonGeometryDeserializer()); + addDeserializer(Distance.class, new DistanceGeometryDeserializer()); + // override TinkerPop's P predicates because of DSE's Search and Geo predicates + addDeserializer(P.class, new DsePJacksonDeserializer()); + } + + @SuppressWarnings("rawtypes") + @Override + public Map getTypeDefinitions() { + Map definitions = new HashMap<>(); + definitions.put(DefaultLineString.class, "LineString"); + definitions.put(DefaultPoint.class, "Point"); + definitions.put(DefaultPolygon.class, "Polygon"); + definitions.put(byte[].class, "Blob"); + definitions.put(Distance.class, "Distance"); + definitions.put(P.class, "P"); + return definitions; + } + + @Override + public String getTypeNamespace() { + return "dse"; + } + + abstract static class AbstractGeometryJacksonDeserializer + extends StdDeserializer { + + private static final long serialVersionUID = 1L; + + AbstractGeometryJacksonDeserializer(final Class clazz) { + super(clazz); + } + + public abstract T parse(final String val); + + @Override + public T deserialize( + final JsonParser jsonParser, final DeserializationContext deserializationContext) + throws IOException { + return parse(jsonParser.getText()); + } + } + + abstract static class AbstractGeometryJacksonSerializer + extends StdScalarSerializer { + + private static final long serialVersionUID = 1L; + + AbstractGeometryJacksonSerializer(final Class clazz) { + super(clazz); + } + + @Override + public void serialize( + final T value, final JsonGenerator gen, final SerializerProvider serializerProvider) + throws IOException { + gen.writeString(value.asWellKnownText()); + } + } + + public static class LineStringGeometrySerializer + extends AbstractGeometryJacksonSerializer { + + private static final long serialVersionUID = 1L; + + LineStringGeometrySerializer() { + super(LineString.class); + } + } + + public static class LineStringGeometryDeserializer + extends AbstractGeometryJacksonDeserializer { + + private static final long serialVersionUID = 1L; + + LineStringGeometryDeserializer() { + super(DefaultLineString.class); + } + + @Override + public DefaultLineString parse(final String val) { + return (DefaultLineString) LineString.fromWellKnownText(val); + } + } + + public static class PolygonGeometrySerializer + extends AbstractGeometryJacksonSerializer { + + private static final long serialVersionUID = 1L; + + PolygonGeometrySerializer() { + super(Polygon.class); + } + } + + public static class PolygonGeometryDeserializer + extends AbstractGeometryJacksonDeserializer { + + private static final long serialVersionUID = 1L; + + PolygonGeometryDeserializer() { + super(DefaultPolygon.class); + } + + @Override + public DefaultPolygon parse(final String val) { + return (DefaultPolygon) Polygon.fromWellKnownText(val); + } + } + + public static class PointGeometrySerializer extends AbstractGeometryJacksonSerializer { + + private static final long serialVersionUID = 1L; + + PointGeometrySerializer() { + super(Point.class); + } + } + + public static class PointGeometryDeserializer + extends AbstractGeometryJacksonDeserializer { + + private static final long serialVersionUID = 1L; + + PointGeometryDeserializer() { + super(DefaultPoint.class); + } + + @Override + public DefaultPoint parse(final String val) { + return (DefaultPoint) Point.fromWellKnownText(val); + } + } + + public static class DistanceGeometrySerializer + extends AbstractGeometryJacksonSerializer { + + private static final long serialVersionUID = 1L; + + DistanceGeometrySerializer() { + super(Distance.class); + } + } + + public static class DistanceGeometryDeserializer + extends AbstractGeometryJacksonDeserializer { + + private static final long serialVersionUID = 1L; + + DistanceGeometryDeserializer() { + super(Distance.class); + } + + @Override + public Distance parse(final String val) { + return Distance.fromWellKnownText(val); + } + } + + @SuppressWarnings("rawtypes") + static final class DsePJacksonSerializer extends StdScalarSerializer

{ + + private static final long serialVersionUID = 1L; + + DsePJacksonSerializer() { + super(P.class); + } + + @Override + public void serialize( + final P p, final JsonGenerator jsonGenerator, final SerializerProvider serializerProvider) + throws IOException { + jsonGenerator.writeStartObject(); + jsonGenerator.writeStringField("predicateType", getPredicateType(p)); + jsonGenerator.writeStringField( + GraphSONTokens.PREDICATE, + p instanceof ConnectiveP + ? p instanceof AndP ? GraphSONTokens.AND : GraphSONTokens.OR + : p.getBiPredicate().toString()); + if (p instanceof ConnectiveP) { + jsonGenerator.writeArrayFieldStart(GraphSONTokens.VALUE); + for (final P predicate : ((ConnectiveP) p).getPredicates()) { + jsonGenerator.writeObject(predicate); + } + jsonGenerator.writeEndArray(); + } else { + if (p.getValue() instanceof Collection) { + jsonGenerator.writeArrayFieldStart(GraphSONTokens.VALUE); + for (final Object object : (Collection) p.getValue()) { + jsonGenerator.writeObject(object); + } + jsonGenerator.writeEndArray(); + } else { + jsonGenerator.writeObjectField(GraphSONTokens.VALUE, p.getValue()); + } + } + jsonGenerator.writeEndObject(); + } + + private String getPredicateType(P p) { + if (p.getBiPredicate() instanceof SearchPredicate) { + return Search.class.getSimpleName(); + } else if (p.getBiPredicate() instanceof GeoPredicate) { + return Geo.class.getSimpleName(); + } else { + return P.class.getSimpleName(); + } + } + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + static final class DsePJacksonDeserializer extends AbstractObjectDeserializer

{ + + private static final long serialVersionUID = 1L; + + DsePJacksonDeserializer() { + super(P.class); + } + + @Override + public P createObject(final Map data) { + final String predicate = (String) data.get(GraphSONTokens.PREDICATE); + final String predicateType = (String) data.get("predicateType"); + final Object value = data.get(GraphSONTokens.VALUE); + if (predicate.equals(GraphSONTokens.AND) || predicate.equals(GraphSONTokens.OR)) { + return predicate.equals(GraphSONTokens.AND) + ? new AndP((List

) value) + : new OrP((List

) value); + } else { + try { + if (value instanceof Collection) { + if (predicate.equals("between")) { + return P.between(((List) value).get(0), ((List) value).get(1)); + } else if (predicateType.equals(P.class.getSimpleName()) + && predicate.equals("inside")) { + return P.between(((List) value).get(0), ((List) value).get(1)); + } else if (predicate.equals("outside")) { + return P.outside(((List) value).get(0), ((List) value).get(1)); + } else if (predicate.equals("within")) { + return P.within((Collection) value); + } else if (predicate.equals("without")) { + return P.without((Collection) value); + } else { + return (P) + P.class.getMethod(predicate, Collection.class).invoke(null, (Collection) value); + } + } else { + if (predicate.equals(SearchPredicate.prefix.name())) { + return Search.prefix((String) value); + } else if (predicate.equals(SearchPredicate.tokenPrefix.name())) { + return Search.tokenPrefix((String) value); + } else if (predicate.equals(SearchPredicate.regex.name())) { + return Search.regex((String) value); + } else if (predicate.equals(SearchPredicate.tokenRegex.name())) { + return Search.tokenRegex((String) value); + } else if (predicate.equals(SearchPredicate.token.name())) { + return Search.token((String) value); + } else if (predicate.equals(SearchPredicate.fuzzy.name())) { + Map arguments = (Map) value; + return Search.fuzzy( + (String) arguments.get("query"), (int) arguments.get("distance")); + } else if (predicate.equals(SearchPredicate.tokenFuzzy.name())) { + Map arguments = (Map) value; + return Search.tokenFuzzy( + (String) arguments.get("query"), (int) arguments.get("distance")); + } else if (predicate.equals(SearchPredicate.phrase.name())) { + Map arguments = (Map) value; + return Search.phrase( + (String) arguments.get("query"), (int) arguments.get("distance")); + } else if (predicateType.equals(Geo.class.getSimpleName()) + && predicate.equals(GeoPredicate.inside.name())) { + return Geo.inside( + ((Distance) value).getCenter(), + ((Distance) value).getRadius(), + Geo.Unit.DEGREES); + } else if (predicateType.equals(Geo.class.getSimpleName()) + && predicate.equals(GeoPredicate.insideCartesian.name())) { + return Geo.inside(((Distance) value).getCenter(), ((Distance) value).getRadius()); + } else { + return (P) P.class.getMethod(predicate, Object.class).invoke(null, value); + } + } + } catch (final Exception e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + } + } + + public static class EditDistanceSerializer extends StdSerializer { + + private static final long serialVersionUID = 1L; + + EditDistanceSerializer() { + super(EditDistance.class); + } + + @Override + public void serialize( + EditDistance editDistance, JsonGenerator generator, SerializerProvider provider) + throws IOException { + generator.writeObject( + ImmutableMap.of("query", editDistance.query, "distance", editDistance.distance)); + } + + @Override + public void serializeWithType( + EditDistance editDistance, + JsonGenerator generator, + SerializerProvider provider, + TypeSerializer serializer) + throws IOException { + serialize(editDistance, generator, provider); + } + } + } + + public static class DriverObjectsModule extends SimpleModule { + + private static final long serialVersionUID = 1L; + + public DriverObjectsModule() { + super("datastax-driver-module"); + addSerializer(ObjectGraphNode.class, new ObjectGraphNodeGraphSON2Serializer()); + } + + static final class ObjectGraphNodeGraphSON2Serializer extends StdSerializer { + + private static final long serialVersionUID = 1L; + + protected ObjectGraphNodeGraphSON2Serializer() { + super(ObjectGraphNode.class); + } + + @Override + public void serialize( + ObjectGraphNode objectGraphNode, + JsonGenerator jsonGenerator, + SerializerProvider serializerProvider) + throws IOException { + jsonGenerator.writeObject(objectGraphNode.as(Object.class)); + } + + @Override + public void serializeWithType( + ObjectGraphNode objectGraphNode, + JsonGenerator jsonGenerator, + SerializerProvider serializerProvider, + TypeSerializer typeSerializer) + throws IOException { + serialize(objectGraphNode, jsonGenerator, serializerProvider); + } + } + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GraphSONUtils.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GraphSONUtils.java new file mode 100644 index 00000000000..02b35f7ee36 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GraphSONUtils.java @@ -0,0 +1,136 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph; + +import com.datastax.dse.driver.api.core.graph.GraphNode; +import com.datastax.oss.driver.api.core.CqlSession; +import com.datastax.oss.driver.shaded.guava.common.base.Suppliers; +import com.datastax.oss.driver.shaded.guava.common.base.Throwables; +import com.datastax.oss.driver.shaded.guava.common.cache.CacheBuilder; +import com.datastax.oss.driver.shaded.guava.common.cache.CacheLoader; +import com.datastax.oss.driver.shaded.guava.common.cache.LoadingCache; +import com.datastax.oss.protocol.internal.util.Bytes; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.function.Supplier; +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONMapper; +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONReader; +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONVersion; +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONXModuleV2d0; +import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerIoRegistryV2d0; +import org.apache.tinkerpop.shaded.jackson.core.Version; +import org.apache.tinkerpop.shaded.jackson.databind.ObjectMapper; + +public class GraphSONUtils { + + private static final LoadingCache OBJECT_MAPPERS = + CacheBuilder.newBuilder() + .build( + new CacheLoader() { + @Override + public ObjectMapper load(@NonNull GraphProtocol graphSubProtocol) throws Exception { + switch (graphSubProtocol) { + case GRAPHSON_1_0: + com.datastax.oss.driver.api.core.Version driverVersion = + CqlSession.OSS_DRIVER_COORDINATES.getVersion(); + Version driverJacksonVersion = + new Version( + driverVersion.getMajor(), + driverVersion.getMinor(), + driverVersion.getPatch(), + driverVersion.getPreReleaseLabels() != null + && driverVersion.getPreReleaseLabels().contains("SNAPSHOT") + ? "SNAPSHOT" + : null, + "com.datastax.dse", + "dse-java-driver-core"); + + ObjectMapper mapper = + GraphSONMapper.build() + .version(GraphSONVersion.V1_0) + .create() + .createMapper(); + mapper.registerModule( + new GraphSON1SerdeTP.GraphSON1DefaultModule( + "graph-graphson1default", driverJacksonVersion)); + mapper.registerModule( + new GraphSON1SerdeTP.GraphSON1JavaTimeModule( + "graph-graphson1javatime", driverJacksonVersion)); + + return mapper; + case GRAPHSON_2_0: + return GraphSONMapper.build() + .version(GraphSONVersion.V2_0) + .addCustomModule(GraphSONXModuleV2d0.build().create(false)) + .addRegistry(TinkerIoRegistryV2d0.instance()) + .addCustomModule(new GraphSON2SerdeTP.DseGraphModule()) + .addCustomModule(new GraphSON2SerdeTP.DriverObjectsModule()) + .create() + .createMapper(); + + default: + throw new IllegalStateException( + String.format("GraphSON sub-protocol unknown: {%s}", graphSubProtocol)); + } + } + }); + + static final Supplier GRAPHSON1_READER = + Suppliers.memoize( + () -> + GraphSONReader.build() + .mapper(GraphSONMapper.build().version(GraphSONVersion.V1_0).create()) + .create()); + + public static ByteBuffer serializeToByteBuffer(Object object, GraphProtocol graphSubProtocol) + throws IOException { + return ByteBuffer.wrap(serializeToBytes(object, graphSubProtocol)); + } + + static byte[] serializeToBytes(Object object, GraphProtocol graphSubProtocol) throws IOException { + try { + return OBJECT_MAPPERS.get(graphSubProtocol).writeValueAsBytes(object); + } catch (ExecutionException e) { + Throwables.throwIfUnchecked(e); + throw new RuntimeException(e); + } + } + + public static GraphNode createGraphNode(List data, GraphProtocol graphSubProtocol) + throws IOException { + try { + ObjectMapper mapper = OBJECT_MAPPERS.get(graphSubProtocol); + switch (graphSubProtocol) { + case GRAPHSON_1_0: + return new LegacyGraphNode(mapper.readTree(Bytes.getArray(data.get(0))), mapper); + case GRAPHSON_2_0: + return new ObjectGraphNode(mapper.readValue(Bytes.getArray(data.get(0)), Object.class)); + default: + // Should already be caught when we lookup in the cache + throw new AssertionError( + String.format("Unknown GraphSON sub-protocol: {%s}", graphSubProtocol)); + } + } catch (ExecutionException e) { + Throwables.throwIfUnchecked(e); + throw new RuntimeException(e); + } + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GraphStatementBase.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GraphStatementBase.java new file mode 100644 index 00000000000..b8baa2f5e49 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GraphStatementBase.java @@ -0,0 +1,413 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph; + +import com.datastax.dse.driver.api.core.graph.GraphStatement; +import com.datastax.oss.driver.api.core.ConsistencyLevel; +import com.datastax.oss.driver.api.core.config.DriverExecutionProfile; +import com.datastax.oss.driver.api.core.metadata.Node; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.nio.ByteBuffer; +import java.time.Duration; +import java.util.Map; +import net.jcip.annotations.Immutable; + +@Immutable +public abstract class GraphStatementBase> + implements GraphStatement { + private final Boolean isIdempotent; + private final Duration timeout; + private final Node node; + private final long timestamp; + private final DriverExecutionProfile executionProfile; + private final String executionProfileName; + private final Map customPayload; + private final String graphName; + private final String traversalSource; + private final String subProtocol; + private final ConsistencyLevel consistencyLevel; + private final ConsistencyLevel readConsistencyLevel; + private final ConsistencyLevel writeConsistencyLevel; + + protected GraphStatementBase( + Boolean isIdempotent, + Duration timeout, + Node node, + long timestamp, + DriverExecutionProfile executionProfile, + String executionProfileName, + Map customPayload, + String graphName, + String traversalSource, + String subProtocol, + ConsistencyLevel consistencyLevel, + ConsistencyLevel readConsistencyLevel, + ConsistencyLevel writeConsistencyLevel) { + this.isIdempotent = isIdempotent; + this.timeout = timeout; + this.node = node; + this.timestamp = timestamp; + this.executionProfile = executionProfile; + this.executionProfileName = executionProfileName; + this.customPayload = customPayload; + this.graphName = graphName; + this.traversalSource = traversalSource; + this.subProtocol = subProtocol; + this.consistencyLevel = consistencyLevel; + this.readConsistencyLevel = readConsistencyLevel; + this.writeConsistencyLevel = writeConsistencyLevel; + } + + protected abstract SelfT newInstance( + Boolean isIdempotent, + Duration timeout, + Node node, + long timestamp, + DriverExecutionProfile executionProfile, + String executionProfileName, + Map customPayload, + String graphName, + String traversalSource, + String subProtocol, + ConsistencyLevel consistencyLevel, + ConsistencyLevel readConsistencyLevel, + ConsistencyLevel writeConsistencyLevel); + + @Override + public Boolean isIdempotent() { + return isIdempotent; + } + + @NonNull + @Override + public SelfT setIdempotent(@Nullable Boolean newIdempotence) { + return newInstance( + newIdempotence, + timeout, + node, + timestamp, + executionProfile, + executionProfileName, + customPayload, + graphName, + traversalSource, + subProtocol, + consistencyLevel, + readConsistencyLevel, + writeConsistencyLevel); + } + + @Nullable + @Override + public Duration getTimeout() { + return timeout; + } + + @NonNull + @Override + public SelfT setTimeout(@Nullable Duration newTimeout) { + return newInstance( + isIdempotent, + newTimeout, + node, + timestamp, + executionProfile, + executionProfileName, + customPayload, + graphName, + traversalSource, + subProtocol, + consistencyLevel, + readConsistencyLevel, + writeConsistencyLevel); + } + + @Nullable + @Override + public Node getNode() { + return node; + } + + @NonNull + @Override + public SelfT setNode(@Nullable Node newNode) { + return newInstance( + isIdempotent, + timeout, + newNode, + timestamp, + executionProfile, + executionProfileName, + customPayload, + graphName, + traversalSource, + subProtocol, + consistencyLevel, + readConsistencyLevel, + writeConsistencyLevel); + } + + @Override + public long getTimestamp() { + return this.timestamp; + } + + @NonNull + @Override + public SelfT setTimestamp(long newTimestamp) { + return newInstance( + isIdempotent, + timeout, + node, + newTimestamp, + executionProfile, + executionProfileName, + customPayload, + graphName, + traversalSource, + subProtocol, + consistencyLevel, + readConsistencyLevel, + writeConsistencyLevel); + } + + @Nullable + @Override + public DriverExecutionProfile getExecutionProfile() { + return executionProfile; + } + + @NonNull + @Override + public SelfT setExecutionProfile(@Nullable DriverExecutionProfile newExecutionProfile) { + return newInstance( + isIdempotent, + timeout, + node, + timestamp, + newExecutionProfile, + executionProfileName, + customPayload, + graphName, + traversalSource, + subProtocol, + consistencyLevel, + readConsistencyLevel, + writeConsistencyLevel); + } + + @Nullable + @Override + public String getExecutionProfileName() { + return executionProfileName; + } + + @NonNull + @Override + public SelfT setExecutionProfileName(@Nullable String newExecutionProfileName) { + return newInstance( + isIdempotent, + timeout, + node, + timestamp, + executionProfile, + newExecutionProfileName, + customPayload, + graphName, + traversalSource, + subProtocol, + consistencyLevel, + readConsistencyLevel, + writeConsistencyLevel); + } + + @NonNull + @Override + public Map getCustomPayload() { + return customPayload; + } + + @NonNull + @Override + public SelfT setCustomPayload(@NonNull Map newCustomPayload) { + return newInstance( + isIdempotent, + timeout, + node, + timestamp, + executionProfile, + executionProfileName, + newCustomPayload, + graphName, + traversalSource, + subProtocol, + consistencyLevel, + readConsistencyLevel, + writeConsistencyLevel); + } + + @Nullable + @Override + public String getGraphName() { + return graphName; + } + + @NonNull + @Override + public SelfT setGraphName(@Nullable String newGraphName) { + return newInstance( + isIdempotent, + timeout, + node, + timestamp, + executionProfile, + executionProfileName, + customPayload, + newGraphName, + traversalSource, + subProtocol, + consistencyLevel, + readConsistencyLevel, + writeConsistencyLevel); + } + + @Nullable + @Override + public String getTraversalSource() { + return traversalSource; + } + + @NonNull + @Override + public SelfT setTraversalSource(@Nullable String newTraversalSource) { + return newInstance( + isIdempotent, + timeout, + node, + timestamp, + executionProfile, + executionProfileName, + customPayload, + graphName, + newTraversalSource, + subProtocol, + consistencyLevel, + readConsistencyLevel, + writeConsistencyLevel); + } + + @Nullable + @Override + public String getSubProtocol() { + return subProtocol; + } + + @NonNull + @Override + public SelfT setSubProtocol(@Nullable String newSubProtocol) { + return newInstance( + isIdempotent, + timeout, + node, + timestamp, + executionProfile, + executionProfileName, + customPayload, + graphName, + traversalSource, + newSubProtocol, + consistencyLevel, + readConsistencyLevel, + writeConsistencyLevel); + } + + @Nullable + @Override + public ConsistencyLevel getConsistencyLevel() { + return consistencyLevel; + } + + @Override + public SelfT setConsistencyLevel(@Nullable ConsistencyLevel newConsistencyLevel) { + return newInstance( + isIdempotent, + timeout, + node, + timestamp, + executionProfile, + executionProfileName, + customPayload, + graphName, + traversalSource, + subProtocol, + newConsistencyLevel, + readConsistencyLevel, + writeConsistencyLevel); + } + + @Nullable + @Override + public ConsistencyLevel getReadConsistencyLevel() { + return readConsistencyLevel; + } + + @NonNull + @Override + public SelfT setReadConsistencyLevel(@Nullable ConsistencyLevel newReadConsistencyLevel) { + return newInstance( + isIdempotent, + timeout, + node, + timestamp, + executionProfile, + executionProfileName, + customPayload, + graphName, + traversalSource, + subProtocol, + consistencyLevel, + newReadConsistencyLevel, + writeConsistencyLevel); + } + + @Nullable + @Override + public ConsistencyLevel getWriteConsistencyLevel() { + return writeConsistencyLevel; + } + + @NonNull + @Override + public SelfT setWriteConsistencyLevel(@Nullable ConsistencyLevel newWriteConsistencyLevel) { + return newInstance( + isIdempotent, + timeout, + node, + timestamp, + executionProfile, + executionProfileName, + customPayload, + graphName, + traversalSource, + subProtocol, + consistencyLevel, + readConsistencyLevel, + newWriteConsistencyLevel); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GraphSupportChecker.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GraphSupportChecker.java new file mode 100644 index 00000000000..6e586bbcf3f --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/GraphSupportChecker.java @@ -0,0 +1,176 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph; + +import com.datastax.dse.driver.api.core.config.DseDriverOption; +import com.datastax.dse.driver.api.core.graph.GraphStatement; +import com.datastax.dse.driver.api.core.graph.PagingEnabledOptions; +import com.datastax.dse.driver.api.core.metadata.DseNodeProperties; +import com.datastax.dse.driver.internal.core.DseProtocolFeature; +import com.datastax.oss.driver.api.core.ProtocolVersion; +import com.datastax.oss.driver.api.core.Version; +import com.datastax.oss.driver.api.core.config.DriverExecutionProfile; +import com.datastax.oss.driver.api.core.metadata.Node; +import com.datastax.oss.driver.internal.core.context.InternalDriverContext; +import com.datastax.oss.driver.internal.core.cql.Conversions; +import com.datastax.oss.driver.shaded.guava.common.annotations.VisibleForTesting; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.Collection; +import java.util.Objects; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class GraphSupportChecker { + + private static final Logger LOG = LoggerFactory.getLogger(GraphSupportChecker.class); + + /** + * The minimum DSE version supporting both graph paging and the GraphBinary sub-protocol is DSE + * 6.8. + */ + private static final Version MIN_DSE_VERSION_GRAPH_BINARY_AND_PAGING = + Objects.requireNonNull(Version.parse("6.8.0")); + + private volatile Boolean contextGraphPagingEnabled; + private volatile Boolean isDse68OrAbove; + + /** + * Checks whether graph paging is available. + * + *

Graph paging is available if: + * + *

    + *
  1. Continuous paging is generally available (this implies protocol version {@link + * com.datastax.dse.driver.api.core.DseProtocolVersion#DSE_V1 DSE_V1} or higher); + *
  2. Graph paging is set to ENABLED or AUTO in the configuration + * with {@link DseDriverOption#GRAPH_PAGING_ENABLED}; + *
  3. If graph paging is set to AUTO, then a check will be performed to verify + * that all hosts are running DSE 6.8+; if that is the case, then graph paging will be + * assumed to be available. + *
+ * + * Note that the hosts check will be done only once, then memoized; if other hosts join the + * cluster later and do not support graph paging, the user has to manually disable graph paging. + */ + public boolean isPagingEnabled( + @NonNull GraphStatement graphStatement, @NonNull InternalDriverContext context) { + DriverExecutionProfile driverExecutionProfile = + Conversions.resolveExecutionProfile(graphStatement, context); + PagingEnabledOptions pagingEnabledOptions = + PagingEnabledOptions.valueOf( + driverExecutionProfile.getString(DseDriverOption.GRAPH_PAGING_ENABLED)); + if (LOG.isTraceEnabled()) { + LOG.trace("GRAPH_PAGING_ENABLED: {}", pagingEnabledOptions); + } + if (pagingEnabledOptions == PagingEnabledOptions.DISABLED) { + return false; + } else if (pagingEnabledOptions == PagingEnabledOptions.ENABLED) { + return true; + } else { + return isContextGraphPagingEnabled(context); + } + } + + /** + * Infers the {@link GraphProtocol} to use to execute the given statement. + * + *

The graph protocol is computed as follows: + * + *

    + *
  1. If the statement declares the protocol to use with {@link + * GraphStatement#getSubProtocol()}, then that protocol is returned. + *
  2. If the driver configuration explicitly defines the protocol to use (see {@link + * DseDriverOption#GRAPH_SUB_PROTOCOL} and reference.conf), then that protocol is returned. + *
  3. Otherwise, the graph protocol to use is determined by the DSE version of hosts in the + * cluster. If any host has DSE version 6.7.x or lower, the default graph protocol is {@link + * GraphProtocol#GRAPHSON_2_0}. If all hosts have DSE version 6.8.0 or higher, the default + * graph protocol is {@link GraphProtocol#GRAPH_BINARY_1_0}. + *
+ * + * Note that the hosts check will be done only once, then memoized; if other hosts join the and do + * not support the computed graph protocol, the user has to manually set the graph protocol to + * use. + * + *

Also note that GRAPH_BINARY_1_0 can only be used with "core" graph engines; if + * you are targeting a "classic" graph engine instead, the user has to manually set the graph + * protocol to something else. + */ + @NonNull + public GraphProtocol inferGraphProtocol( + @NonNull GraphStatement statement, + @NonNull DriverExecutionProfile config, + @NonNull InternalDriverContext context) { + String graphProtocol = statement.getSubProtocol(); + if (graphProtocol == null) { + // use the protocol specified in configuration, otherwise get the default from the context + graphProtocol = + config.isDefined(DseDriverOption.GRAPH_SUB_PROTOCOL) + ? config.getString(DseDriverOption.GRAPH_SUB_PROTOCOL) + : getDefaultGraphProtocol(context).toInternalCode(); + } + // should not be null because we call config.getString() with a default value + Objects.requireNonNull( + graphProtocol, + "Could not determine the graph protocol for the query. This is a bug, please report."); + + return GraphProtocol.fromString(graphProtocol); + } + + private boolean isContextGraphPagingEnabled(InternalDriverContext context) { + if (contextGraphPagingEnabled == null) { + ProtocolVersion protocolVersion = context.getProtocolVersion(); + if (!context + .getProtocolVersionRegistry() + .supports(protocolVersion, DseProtocolFeature.CONTINUOUS_PAGING)) { + contextGraphPagingEnabled = false; + } else { + if (isDse68OrAbove == null) { + isDse68OrAbove = checkIsDse68OrAbove(context); + } + contextGraphPagingEnabled = isDse68OrAbove; + } + } + return contextGraphPagingEnabled; + } + + /** + * Determines the default {@link GraphProtocol} for the given context. + * + * @return The default GraphProtocol to used based on the provided context. + */ + @VisibleForTesting + GraphProtocol getDefaultGraphProtocol(@NonNull InternalDriverContext context) { + if (isDse68OrAbove == null) { + isDse68OrAbove = checkIsDse68OrAbove(context); + } + // if the DSE version can't be determined, default to GraphSON 2.0 + return isDse68OrAbove ? GraphProtocol.GRAPH_BINARY_1_0 : GraphProtocol.GRAPHSON_2_0; + } + + private boolean checkIsDse68OrAbove(@NonNull InternalDriverContext context) { + Collection nodes = context.getMetadataManager().getMetadata().getNodes().values(); + + for (Node node : nodes) { + Version dseVersion = (Version) node.getExtras().get(DseNodeProperties.DSE_VERSION); + if (dseVersion == null || dseVersion.compareTo(MIN_DSE_VERSION_GRAPH_BINARY_AND_PAGING) < 0) { + return false; + } + } + return true; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/LegacyGraphNode.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/LegacyGraphNode.java new file mode 100644 index 00000000000..1749bf00873 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/LegacyGraphNode.java @@ -0,0 +1,323 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph; + +import com.datastax.dse.driver.api.core.graph.GraphNode; +import com.datastax.oss.driver.api.core.type.reflect.GenericType; +import com.datastax.oss.driver.shaded.guava.common.base.Objects; +import com.datastax.oss.driver.shaded.guava.common.base.Preconditions; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map; +import java.util.Set; +import net.jcip.annotations.Immutable; +import org.apache.tinkerpop.gremlin.process.traversal.Path; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Property; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.structure.VertexProperty; +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONTokens; +import org.apache.tinkerpop.gremlin.structure.util.Attachable; +import org.apache.tinkerpop.shaded.jackson.core.JsonParser; +import org.apache.tinkerpop.shaded.jackson.databind.JavaType; +import org.apache.tinkerpop.shaded.jackson.databind.JsonNode; +import org.apache.tinkerpop.shaded.jackson.databind.ObjectMapper; + +/** + * Legacy implementation for GraphSON 1 results. + * + *

The server returns plain JSON with no type information. The driver works with the JSON + * representation directly. + */ +@Immutable +public class LegacyGraphNode implements GraphNode { + private static final String TYPE = "type"; + private static final String VERTEX_TYPE = "vertex"; + private static final String EDGE_TYPE = "edge"; + + private static final GenericType> LIST_TYPE = GenericType.listOf(Object.class); + private static final GenericType> MAP_TYPE = + GenericType.mapOf(String.class, Object.class); + + private final JsonNode delegate; + private final ObjectMapper objectMapper; + + public LegacyGraphNode(JsonNode delegate, ObjectMapper objectMapper) { + Preconditions.checkNotNull(delegate); + Preconditions.checkNotNull(objectMapper); + this.delegate = delegate; + this.objectMapper = objectMapper; + } + + /** + * The underlying JSON representation. + * + *

This is an implementation detail, it's only exposed through the internal API. + */ + public JsonNode getDelegate() { + return delegate; + } + + /** + * The object mapper used to deserialize results in {@link #as(Class)} and {@link + * #as(GenericType)}. + * + *

This is an implementation detail, it's only exposed through the internal API. + */ + public ObjectMapper getObjectMapper() { + return objectMapper; + } + + @Override + public boolean isNull() { + return delegate.isNull(); + } + + @Override + public boolean isMap() { + return delegate.isObject(); + } + + @Override + public Iterable keys() { + return (Iterable) delegate::fieldNames; + } + + @Override + public LegacyGraphNode getByKey(Object key) { + if (!(key instanceof String)) { + return null; + } + JsonNode node = delegate.get(((String) key)); + if (node == null) { + return null; + } + return new LegacyGraphNode(node, objectMapper); + } + + @Override + @SuppressWarnings("unchecked") + public Map asMap() { + return (Map) as(MAP_TYPE); + } + + @Override + public boolean isList() { + return delegate.isArray(); + } + + @Override + public int size() { + return delegate.size(); + } + + @Override + public LegacyGraphNode getByIndex(int index) { + JsonNode node = delegate.get(index); + if (node == null) { + return null; + } + return new LegacyGraphNode(node, objectMapper); + } + + @Override + @SuppressWarnings("unchecked") + public List asList() { + return (List) as(LIST_TYPE); + } + + @Override + public boolean isValue() { + return delegate.isValueNode(); + } + + @Override + public int asInt() { + return delegate.asInt(); + } + + @Override + public boolean asBoolean() { + return delegate.asBoolean(); + } + + @Override + public long asLong() { + return delegate.asLong(); + } + + @Override + public double asDouble() { + return delegate.asDouble(); + } + + @Override + public String asString() { + return delegate.asText(); + } + + @Override + public boolean isVertex() { + return isType(VERTEX_TYPE); + } + + @Override + public Vertex asVertex() { + try { + return GraphSONUtils.GRAPHSON1_READER + .get() + .readVertex( + new ByteArrayInputStream(delegate.toString().getBytes(StandardCharsets.UTF_8)), + null, + null, + null); + } catch (IOException e) { + throw new UncheckedIOException("Could not deserialize node as Vertex.", e); + } + } + + @Override + public boolean isEdge() { + return isType(EDGE_TYPE); + } + + @Override + public Edge asEdge() { + try { + return GraphSONUtils.GRAPHSON1_READER + .get() + .readEdge( + new ByteArrayInputStream(delegate.toString().getBytes(StandardCharsets.UTF_8)), + Attachable::get); + } catch (IOException e) { + throw new UncheckedIOException("Could not deserialize node as Edge.", e); + } + } + + @Override + public boolean isPath() { + return false; + } + + @Override + public Path asPath() { + throw new UnsupportedOperationException( + "GraphSON1 does not support Path, use another Graph sub-protocol such as GraphSON2."); + } + + @Override + public boolean isProperty() { + return delegate.has(GraphSONTokens.KEY) && delegate.has(GraphSONTokens.VALUE); + } + + @Override + @SuppressWarnings("unchecked") + public Property asProperty() { + try { + return GraphSONUtils.GRAPHSON1_READER + .get() + .readProperty( + new ByteArrayInputStream(delegate.toString().getBytes(StandardCharsets.UTF_8)), + Attachable::get); + } catch (IOException e) { + throw new UncheckedIOException("Could not deserialize node as Property.", e); + } + } + + @Override + public boolean isVertexProperty() { + return delegate.has(GraphSONTokens.ID) + && delegate.has(GraphSONTokens.VALUE) + && delegate.has(GraphSONTokens.LABEL); + } + + @Override + @SuppressWarnings("unchecked") + public VertexProperty asVertexProperty() { + try { + return GraphSONUtils.GRAPHSON1_READER + .get() + .readVertexProperty( + new ByteArrayInputStream(delegate.toString().getBytes(StandardCharsets.UTF_8)), + Attachable::get); + } catch (IOException e) { + throw new UncheckedIOException("Could not deserialize node as VertexProperty.", e); + } + } + + @Override + public boolean isSet() { + return false; + } + + @Override + public Set asSet() { + throw new UnsupportedOperationException( + "GraphSON1 does not support Set, use another Graph sub-protocol such as GraphSON2."); + } + + @Override + public ResultT as(Class clazz) { + try { + return objectMapper.treeToValue(delegate, clazz); + } catch (IOException e) { + throw new UncheckedIOException("Could not deserialize node as: " + clazz, e); + } + } + + @Override + public ResultT as(GenericType type) { + try { + JsonParser parser = objectMapper.treeAsTokens(delegate); + JavaType javaType = objectMapper.constructType(type.__getToken().getType()); + return objectMapper.readValue(parser, javaType); + } catch (IOException e) { + throw new UncheckedIOException("Could not deserialize node as: " + type, e); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof LegacyGraphNode)) { + return false; + } + LegacyGraphNode that = (LegacyGraphNode) o; + return Objects.equal(delegate, that.delegate); + } + + @Override + public int hashCode() { + return Objects.hashCode(delegate); + } + + @Override + public String toString() { + return delegate.toString(); + } + + private boolean isType(String expectedTypeName) { + JsonNode type = delegate.get(TYPE); + return type != null && expectedTypeName.equals(type.asText()); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/MultiPageGraphResultSet.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/MultiPageGraphResultSet.java new file mode 100644 index 00000000000..fe81d73ba00 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/MultiPageGraphResultSet.java @@ -0,0 +1,123 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph; + +import com.datastax.dse.driver.api.core.graph.AsyncGraphResultSet; +import com.datastax.dse.driver.api.core.graph.GraphNode; +import com.datastax.dse.driver.api.core.graph.GraphResultSet; +import com.datastax.oss.driver.api.core.cql.ExecutionInfo; +import com.datastax.oss.driver.internal.core.util.CountingIterator; +import com.datastax.oss.driver.internal.core.util.concurrent.BlockingOperation; +import com.datastax.oss.driver.internal.core.util.concurrent.CompletableFutures; +import com.datastax.oss.driver.shaded.guava.common.collect.Lists; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public class MultiPageGraphResultSet implements GraphResultSet { + private final RowIterator iterator; + private final List executionInfos = new ArrayList<>(); + + public MultiPageGraphResultSet(AsyncGraphResultSet firstPage) { + iterator = new RowIterator(firstPage); + executionInfos.add(firstPage.getRequestExecutionInfo()); + } + + @Override + public void cancel() { + iterator.cancel(); + } + + @NonNull + @Override + public ExecutionInfo getRequestExecutionInfo() { + return executionInfos.get(executionInfos.size() - 1); + } + + @NonNull + @Override + @Deprecated + public com.datastax.dse.driver.api.core.graph.GraphExecutionInfo getExecutionInfo() { + return GraphExecutionInfoConverter.convert(getRequestExecutionInfo()); + } + + /** + * The execution information for all the queries that have been performed so far to assemble this + * iterable. + * + *

This will have multiple elements if the query is paged, since the driver performs blocking + * background queries to fetch additional pages transparently as the result set is being iterated. + */ + @NonNull + public List getRequestExecutionInfos() { + return executionInfos; + } + + /** @deprecated use {@link #getRequestExecutionInfos()} instead. */ + @NonNull + @Deprecated + public List getExecutionInfos() { + return Lists.transform(executionInfos, GraphExecutionInfoConverter::convert); + } + + @NonNull + @Override + public Iterator iterator() { + return iterator; + } + + public class RowIterator extends CountingIterator { + private AsyncGraphResultSet currentPage; + private Iterator currentRows; + private boolean cancelled = false; + + private RowIterator(AsyncGraphResultSet firstPage) { + super(firstPage.remaining()); + currentPage = firstPage; + currentRows = firstPage.currentPage().iterator(); + } + + @Override + protected GraphNode computeNext() { + maybeMoveToNextPage(); + return currentRows.hasNext() ? currentRows.next() : endOfData(); + } + + private void maybeMoveToNextPage() { + if (!cancelled && !currentRows.hasNext() && currentPage.hasMorePages()) { + BlockingOperation.checkNotDriverThread(); + AsyncGraphResultSet nextPage = + CompletableFutures.getUninterruptibly(currentPage.fetchNextPage()); + currentPage = nextPage; + remaining += currentPage.remaining(); + currentRows = nextPage.currentPage().iterator(); + executionInfos.add(nextPage.getRequestExecutionInfo()); + } + } + + private void cancel() { + currentPage.cancel(); + cancelled = true; + } + + public boolean isCancelled() { + return cancelled; + } + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/ObjectGraphNode.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/ObjectGraphNode.java new file mode 100644 index 00000000000..56123799fdd --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/ObjectGraphNode.java @@ -0,0 +1,244 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph; + +import com.datastax.dse.driver.api.core.graph.GraphNode; +import com.datastax.oss.driver.api.core.type.reflect.GenericType; +import com.datastax.oss.driver.shaded.guava.common.base.Objects; +import java.util.List; +import java.util.Map; +import java.util.Set; +import net.jcip.annotations.Immutable; +import org.apache.tinkerpop.gremlin.process.traversal.Path; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Property; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.structure.VertexProperty; + +/** + * Modern implementation for GraphSON 2+ results. + * + *

The server returns results with type information. The driver works with the decoded objects + * directly. + */ +@Immutable +public class ObjectGraphNode implements GraphNode { + + private final Object delegate; + + public ObjectGraphNode(Object delegate) { + this.delegate = delegate; + } + + @Override + public boolean isNull() { + return delegate == null; + } + + @Override + public boolean isMap() { + return delegate instanceof Map; + } + + @Override + public Iterable keys() { + return ((Map) delegate).keySet(); + } + + @Override + public GraphNode getByKey(Object key) { + if (!isMap()) { + return null; + } + Map map = asMap(); + if (map.containsKey(key)) { + return new ObjectGraphNode(map.get(key)); + } + return null; + } + + @Override + @SuppressWarnings("unchecked") + public Map asMap() { + return (Map) delegate; + } + + @Override + public boolean isList() { + return delegate instanceof List; + } + + @Override + public int size() { + if (isList()) { + return asList().size(); + } else if (isMap()) { + return asMap().size(); + } else if (isSet()) { + return asSet().size(); + } else { + return 0; + } + } + + @Override + public GraphNode getByIndex(int index) { + if (!isList() || index < 0 || index >= size()) { + return null; + } + return new ObjectGraphNode(asList().get(index)); + } + + @Override + @SuppressWarnings("unchecked") + public List asList() { + return (List) delegate; + } + + @Override + public boolean isValue() { + return !(isList() + || isMap() + || isSet() + || isVertex() + || isEdge() + || isPath() + || isProperty() + || isVertexProperty()); + } + + @Override + public boolean isVertexProperty() { + return delegate instanceof VertexProperty; + } + + @Override + public boolean isProperty() { + return delegate instanceof Property; + } + + @Override + public boolean isPath() { + return delegate instanceof Path; + } + + @Override + public int asInt() { + return (Integer) delegate; + } + + @Override + public boolean asBoolean() { + return (Boolean) delegate; + } + + @Override + public long asLong() { + return (Long) delegate; + } + + @Override + public double asDouble() { + return (Double) delegate; + } + + @Override + public String asString() { + return (String) delegate; + } + + @Override + @SuppressWarnings("unchecked") + public T as(Class clazz) { + return (T) delegate; + } + + @Override + @SuppressWarnings("unchecked") + public T as(GenericType type) { + return (T) delegate; + } + + @Override + public boolean isVertex() { + return delegate instanceof Vertex; + } + + @Override + public Vertex asVertex() { + return (Vertex) delegate; + } + + @Override + public boolean isEdge() { + return delegate instanceof Edge; + } + + @Override + public Edge asEdge() { + return (Edge) delegate; + } + + @Override + public Path asPath() { + return (Path) delegate; + } + + @Override + @SuppressWarnings("unchecked") + public Property asProperty() { + return (Property) delegate; + } + + @Override + @SuppressWarnings("unchecked") + public VertexProperty asVertexProperty() { + return (VertexProperty) delegate; + } + + @Override + public boolean isSet() { + return delegate instanceof Set; + } + + @Override + @SuppressWarnings("unchecked") + public Set asSet() { + return (Set) delegate; + } + + @Override + public String toString() { + return this.delegate.toString(); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + // Compare each others' delegates. + return other instanceof ObjectGraphNode + && Objects.equal(this.delegate, ((ObjectGraphNode) other).delegate); + } + + @Override + public int hashCode() { + return Objects.hashCode(delegate); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/SearchPredicate.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/SearchPredicate.java new file mode 100644 index 00000000000..b69c3a59cf0 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/SearchPredicate.java @@ -0,0 +1,301 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph; + +import com.datastax.oss.driver.shaded.guava.common.collect.Sets; +import java.util.List; +import java.util.Set; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * List of predicates for geolocation usage with DseGraph and Search indexes. Should not be accessed + * directly but through the {@link com.datastax.dse.driver.api.core.graph.predicates.Search} static + * methods. + */ +public enum SearchPredicate implements DsePredicate { + /** Whether the text contains a given term as a token in the text (case insensitive). */ + token { + @Override + public boolean test(Object value, Object condition) { + preEvaluate(condition); + return value != null && evaluate(value.toString(), (String) condition); + } + + boolean evaluate(String value, String terms) { + Set tokens = Sets.newHashSet(tokenize(value.toLowerCase())); + terms = terms.trim(); + List tokenTerms = tokenize(terms.toLowerCase()); + if (!terms.isEmpty() && tokenTerms.isEmpty()) { + return false; + } + for (String term : tokenTerms) { + if (!tokens.contains(term)) { + return false; + } + } + return true; + } + + @Override + public boolean isValidCondition(Object condition) { + return condition != null && isNotBlank((String) condition); + } + + @Override + public String toString() { + return "token"; + } + }, + + /** Whether the text contains a token that starts with a given term (case insensitive). */ + tokenPrefix { + @Override + public boolean test(Object value, Object condition) { + preEvaluate(condition); + return value != null && evaluate(value.toString(), (String) condition); + } + + boolean evaluate(String value, String prefix) { + for (String token : tokenize(value.toLowerCase())) { + if (token.startsWith(prefix.toLowerCase().trim())) { + return true; + } + } + return false; + } + + @Override + public boolean isValidCondition(Object condition) { + return condition != null; + } + + @Override + public String toString() { + return "tokenPrefix"; + } + }, + + /** Whether the text contains a token that matches a regular expression (case insensitive). */ + tokenRegex { + @Override + public boolean test(Object value, Object condition) { + preEvaluate(condition); + return value != null && evaluate(value.toString(), (String) condition); + } + + boolean evaluate(String value, String regex) { + Pattern compiled = Pattern.compile(regex, Pattern.CASE_INSENSITIVE); + for (String token : tokenize(value.toLowerCase())) { + if (compiled.matcher(token).matches()) { + return true; + } + } + return false; + } + + @Override + public boolean isValidCondition(Object condition) { + return condition != null && isNotBlank((String) condition); + } + + @Override + public String toString() { + return "tokenRegex"; + } + }, + + /** + * Whether some token in the text is within a given edit distance from the given term (case + * insensitive). + */ + tokenFuzzy { + @Override + public boolean test(Object value, Object condition) { + preEvaluate(condition); + if (value == null) { + return false; + } + + EditDistance fuzzyCondition = (EditDistance) condition; + + for (String token : tokenize(value.toString().toLowerCase())) { + if (SearchUtils.getOptimalStringAlignmentDistance(token, fuzzyCondition.query.toLowerCase()) + <= fuzzyCondition.distance) { + return true; + } + } + + return false; + } + + @Override + public boolean isValidCondition(Object condition) { + return condition != null; + } + + @Override + public String toString() { + return "tokenFuzzy"; + } + }, + + /** Whether the text starts with a given prefix (case sensitive). */ + prefix { + @Override + public boolean test(Object value, Object condition) { + preEvaluate(condition); + return value != null && value.toString().startsWith(((String) condition).trim()); + } + + @Override + public boolean isValidCondition(Object condition) { + return condition != null; + } + + @Override + public String toString() { + return "prefix"; + } + }, + + /** Whether the text matches a regular expression (case sensitive). */ + regex { + @Override + public boolean test(Object value, Object condition) { + preEvaluate(condition); + return value != null + && Pattern.compile((String) condition, Pattern.DOTALL) + .matcher(value.toString()) + .matches(); + } + + @Override + public boolean isValidCondition(Object condition) { + return condition != null && isNotBlank((String) condition); + } + + @Override + public String toString() { + return "regex"; + } + }, + + /** Whether the text is within a given edit distance from the given term (case sensitive). */ + fuzzy { + @Override + public boolean test(Object value, Object condition) { + preEvaluate(condition); + if (value == null) { + return false; + } + EditDistance fuzzyCondition = (EditDistance) condition; + return SearchUtils.getOptimalStringAlignmentDistance(value.toString(), fuzzyCondition.query) + <= fuzzyCondition.distance; + } + + @Override + public boolean isValidCondition(Object condition) { + return condition != null; + } + + @Override + public String toString() { + return "fuzzy"; + } + }, + + /** + * Whether tokenized text contains a given phrase, optionally within a given proximity (case + * insensitive). + */ + phrase { + @Override + public boolean test(Object value, Object condition) { + preEvaluate(condition); + if (value == null) { + return false; + } + + EditDistance phraseCondition = (EditDistance) condition; + + List valueTokens = tokenize(value.toString().toLowerCase()); + List phraseTokens = tokenize(phraseCondition.query.toLowerCase()); + + int valuePosition = 0; + int phrasePosition = 0; + int distance = 0; + + // Look for matches while phrase/value tokens and distance budget remain + while (phrasePosition < phraseTokens.size() + && valuePosition < valueTokens.size() + && distance <= phraseCondition.distance) { + + if (phraseTokens.get(phrasePosition).equals(valueTokens.get(valuePosition))) { + // Early return-true when we've matched the whole phrase (within the specified distance) + if (phrasePosition == phraseTokens.size() - 1) { + return true; + } + phrasePosition++; + } else if (0 < phrasePosition) { + // We've previously found at least one matching token in the input string, + // but the current token does not match the phrase. Increment distance. + distance++; + } + + valuePosition++; + } + + return false; + } + + @Override + public boolean isValidCondition(Object condition) { + return condition != null; + } + + @Override + public String toString() { + return "phrase"; + } + }; + + private static boolean isNotBlank(String str) { + if (str == null || str.isEmpty()) { + return false; + } + int strLen = str.length(); + for (int i = 0; i < strLen; i++) { + if (!Character.isWhitespace(str.charAt(i))) { + return true; + } + } + return false; + } + + // Match anything that is not either: + // 1) a unicode letter, regardless of subcategory (same as Character.isLetter), or + // 2) a unicode decimal digit number (same as Character.isDigit) + private static final Pattern TOKEN_SPLIT_PATTERN = Pattern.compile("[^\\p{L}\\p{Nd}]"); + + static List tokenize(String str) { + String[] rawTokens = TOKEN_SPLIT_PATTERN.split(str); // could contain empty strings + return Stream.of(rawTokens).filter(t -> 0 < t.length()).collect(Collectors.toList()); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/SearchUtils.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/SearchUtils.java new file mode 100644 index 00000000000..3440c40e87a --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/SearchUtils.java @@ -0,0 +1,143 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph; + +public class SearchUtils { + + /** + * Finds the Optimal + * string alignment distance – also referred to as the Damerau-Levenshtein distance – between + * two strings. + * + *

This is the number of changes needed to change one string into another (insertions, + * deletions or substitutions of a single character, or transpositions of two adjacent + * characters). + * + *

This implementation is based on the Apache Commons Lang implementation of the Levenshtein + * distance, only adding support for transpositions. + * + *

Note that this is the distance used in Lucene for {@code FuzzyTermsEnum}. Lucene itself has + * an implementation of this algorithm, but it is much less efficient in terms of space (also note + * that Lucene's implementation does not return the distance, but a similarity score based on it). + * + * @param s the first string, must not be {@code null}. + * @param t the second string, must not be {@code null}. + * @return The Optimal string alignment distance between the two strings. + * @throws IllegalArgumentException if either String input is {@code null}. + * @see org.apache.commons.lang.StringUtils#getLevenshteinDistance(String, String) + * @see + * LuceneLevenshteinDistance + */ + public static int getOptimalStringAlignmentDistance(String s, String t) { + + /* + * Code adapted from https://github.com/apache/commons-lang/blob/LANG_2_6/src/main/java/org/apache/commons/lang/StringUtils.java + * which was originally released under the Apache 2.0 license with the following copyright: + * + * 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. + */ + if (s == null || t == null) { + throw new IllegalArgumentException("Strings must not be null"); + } + + int n = s.length(); // length of s + int m = t.length(); // length of t + + if (n == 0) { + return m; + } else if (m == 0) { + return n; + } + + if (n > m) { + // swap the input strings to consume less memory + String tmp = s; + s = t; + t = tmp; + n = m; + m = t.length(); + } + + // instead of maintaining the full matrix in memory, + // we use a sliding window containing 3 lines: + // the current line being written to, and + // the two previous ones. + + int d[] = new int[n + 1]; // current line in the cost matrix + int p1[] = new int[n + 1]; // first line above the current one in the cost matrix + int p2[] = new int[n + 1]; // second line above the current one in the cost matrix + int _d[]; // placeholder to assist in swapping p1, p2 and d + + // indexes into strings s and t + int i; // iterates through s + int j; // iterates through t + + for (i = 0; i <= n; i++) { + p1[i] = i; + } + + for (j = 1; j <= m; j++) { + + // jth character of t + char t_j = t.charAt(j - 1); + d[0] = j; + + for (i = 1; i <= n; i++) { + + char s_i = s.charAt(i - 1); + int cost = s_i == t_j ? 0 : 1; + + int deletion = d[i - 1] + 1; // cell to the left + 1 + int insertion = p1[i] + 1; // cell to the top + 1 + int substitution = p1[i - 1] + cost; // cell diagonally left and up + cost + + d[i] = Math.min(Math.min(deletion, insertion), substitution); + + // transposition + if (i > 1 && j > 1 && s_i == t.charAt(j - 2) && s.charAt(i - 2) == t_j) { + d[i] = Math.min(d[i], p2[i - 2] + cost); + } + } + + // swap arrays + _d = p2; + p2 = p1; + p1 = d; + d = _d; + } + + // our last action in the above loop was to switch d and p1, so p1 now + // actually has the most recent cost counts + return p1[n]; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/SinglePageGraphResultSet.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/SinglePageGraphResultSet.java new file mode 100644 index 00000000000..ff1d984d745 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/SinglePageGraphResultSet.java @@ -0,0 +1,61 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph; + +import com.datastax.dse.driver.api.core.graph.AsyncGraphResultSet; +import com.datastax.dse.driver.api.core.graph.GraphNode; +import com.datastax.dse.driver.api.core.graph.GraphResultSet; +import com.datastax.oss.driver.api.core.cql.ExecutionInfo; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.Iterator; +import net.jcip.annotations.NotThreadSafe; + +@NotThreadSafe +public class SinglePageGraphResultSet implements GraphResultSet { + + private final AsyncGraphResultSet onlyPage; + + public SinglePageGraphResultSet(AsyncGraphResultSet onlyPage) { + this.onlyPage = onlyPage; + assert !onlyPage.hasMorePages(); + } + + @NonNull + @Override + public ExecutionInfo getRequestExecutionInfo() { + return onlyPage.getRequestExecutionInfo(); + } + + @NonNull + @Override + @Deprecated + public com.datastax.dse.driver.api.core.graph.GraphExecutionInfo getExecutionInfo() { + return onlyPage.getExecutionInfo(); + } + + @NonNull + @Override + public Iterator iterator() { + return onlyPage.currentPage().iterator(); + } + + @Override + public void cancel() { + onlyPage.cancel(); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/TinkerpopBufferUtil.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/TinkerpopBufferUtil.java new file mode 100644 index 00000000000..5650d904350 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/TinkerpopBufferUtil.java @@ -0,0 +1,32 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph; + +import java.nio.ByteBuffer; +import org.apache.tinkerpop.gremlin.structure.io.Buffer; + +/** Mirror of {@link ByteBufUtil} for Tinkerpop Buffer's */ +public class TinkerpopBufferUtil { + + public static ByteBuffer readBytes(Buffer tinkerBuff, int size) { + ByteBuffer res = ByteBuffer.allocate(size); + tinkerBuff.readBytes(res); + res.flip(); + return res; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/AbstractDynamicGraphBinaryCustomSerializer.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/AbstractDynamicGraphBinaryCustomSerializer.java new file mode 100644 index 00000000000..649f5310c5d --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/AbstractDynamicGraphBinaryCustomSerializer.java @@ -0,0 +1,74 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph.binary; + +import java.io.IOException; +import org.apache.tinkerpop.gremlin.structure.io.Buffer; +import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryReader; +import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryWriter; + +/** + * Convenience class for dynamic types implemented as Custom types in GraphBinary. This class will + * take care of handling {value_length} automatically for implementing classes. {@link + * #writeDynamicCustomValue(Object, Buffer, GraphBinaryWriter)} and {@link + * #readDynamicCustomValue(Buffer, GraphBinaryReader)} only need to handle writing the internal + * components of the custom type. + * + * @param the java type the implementing classes will encode and decode. + */ +public abstract class AbstractDynamicGraphBinaryCustomSerializer + extends AbstractSimpleGraphBinaryCustomSerializer { + protected abstract void writeDynamicCustomValue(T value, Buffer buffer, GraphBinaryWriter context) + throws IOException; + + protected abstract T readDynamicCustomValue(Buffer buffer, GraphBinaryReader context) + throws IOException; + + @Override + protected T readCustomValue(int valueLength, Buffer buffer, GraphBinaryReader context) + throws IOException { + int initialIndex = buffer.readerIndex(); + + // read actual custom value + T read = readDynamicCustomValue(buffer, context); + + // make sure we didn't read more than what was input as {value_length} + checkValueSize(valueLength, (buffer.readerIndex() - initialIndex)); + + return read; + } + + @Override + protected void writeCustomValue(T value, Buffer buffer, GraphBinaryWriter context) + throws IOException { + // Store the current writer index + final int valueLengthIndex = buffer.writerIndex(); + + // Write a dummy length that will be overwritten at the end of this method + buffer.writeInt(0); + + // Custom type's writer logic + writeDynamicCustomValue(value, buffer, context); + + // value_length = diff written - 4 bytes for the dummy length + final int valueLength = buffer.writerIndex() - valueLengthIndex - GraphBinaryUtils.sizeOfInt(); + + // Go back, write the {value_length} and then reset back the writer index + buffer.markWriterIndex().writerIndex(valueLengthIndex).writeInt(valueLength).resetWriterIndex(); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/AbstractSimpleGraphBinaryCustomSerializer.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/AbstractSimpleGraphBinaryCustomSerializer.java new file mode 100644 index 00000000000..6dd149707e8 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/AbstractSimpleGraphBinaryCustomSerializer.java @@ -0,0 +1,153 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph.binary; + +import com.datastax.oss.driver.shaded.guava.common.base.Preconditions; +import java.io.IOException; +import org.apache.tinkerpop.gremlin.structure.io.Buffer; +import org.apache.tinkerpop.gremlin.structure.io.binary.DataType; +import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryReader; +import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryWriter; +import org.apache.tinkerpop.gremlin.structure.io.binary.types.CustomTypeSerializer; + +/** + * A base custom type serializer for DSE types that handles most of the boiler plate code associated + * with GraphBinary's custom types. + * + *

The full format of a custom type in GraphBinary is the following: + * + *

{type_code}{custom_type_name}{custom_type_info_length}{custom_type_info_bytes}{value_flag}{value_length}{value_bytes} + * + *

This class is made to handle + * {type_code}{custom_type_name}{custom_type_info_length}{custom_type_info_bytes}{value_flag} for + * DSE types. + * + *

Implementing classes are still in charge of encoding {value_length}{value_bytes} in the {@link + * #readCustomValue(int, Buffer, GraphBinaryReader)} implementations. + * + *

Implementing classes must override {@link CustomTypeSerializer#getTypeName()} with their own + * type name. + * + * @param the java type the implementing classes will encode and decode. + */ +abstract class AbstractSimpleGraphBinaryCustomSerializer implements CustomTypeSerializer { + AbstractSimpleGraphBinaryCustomSerializer() { + super(); + } + + protected static final String INCORRECT_VALUE_LENGTH_ERROR_MESSAGE = + "{value_length} read for this value does not correspond to the size of a '%s' value. [%s] bytes required but got [%s]"; + + protected abstract T readCustomValue(int valueLength, Buffer buffer, GraphBinaryReader context) + throws IOException; + + protected abstract void writeCustomValue(T value, Buffer buffer, GraphBinaryWriter context) + throws IOException; + + protected void checkValueSize(int lengthRequired, int lengthFound) { + Preconditions.checkArgument( + lengthFound == lengthRequired, + INCORRECT_VALUE_LENGTH_ERROR_MESSAGE, + getTypeName(), + lengthRequired, + lengthFound); + } + + @Override + public DataType getDataType() { + return DataType.CUSTOM; + } + + @Override + public T read(Buffer buffer, GraphBinaryReader context) throws IOException { + // the type serializer registry will take care of deserializing {custom_type_name} + // read {custom_type_info_length} and verify it is 0. + // See #write(T, ByteBuf, GraphBinaryWriter) for why it is set to 0 + if (context.readValue(buffer, Integer.class, false) != 0) { + throw new IOException("{custom_type_info} should not be provided for this custom type"); + } + + return readValue(buffer, context, true); + } + + @Override + public T readValue(Buffer buffer, GraphBinaryReader context, boolean nullable) + throws IOException { + if (nullable) { + // read {value_flag} + final byte valueFlag = buffer.readByte(); + + // if value is null and the value is nullable + if ((valueFlag & 1) == 1) { + return null; + } + // Note: we don't error out if the valueFlag == "value is null" and nullable == false because + // the serializer + // should have errored out at write time if that was the case. + } + + // Read the byte length of the value bytes + final int valueLength = buffer.readInt(); + + if (valueLength <= 0) { + throw new IOException(String.format("Unexpected value length: %d", valueLength)); + } + + if (valueLength > buffer.readableBytes()) { + throw new IOException( + String.format( + "Not enough readable bytes: %d bytes required for value (%d bytes available)", + valueLength, buffer.readableBytes())); + } + + // subclasses are responsible for reading {value} + return readCustomValue(valueLength, buffer, context); + } + + @Override + public void write(final T value, final Buffer buffer, final GraphBinaryWriter context) + throws IOException { + // the type serializer registry will take care of serializing {custom_type_name} + // write "{custom_type_info_length}" to 0 because we don't need it for the DSE types + context.writeValue(0, buffer, false); + writeValue(value, buffer, context, true); + } + + @Override + public void writeValue( + final T value, final Buffer buffer, final GraphBinaryWriter context, final boolean nullable) + throws IOException { + if (value == null) { + if (!nullable) { + throw new IOException("Unexpected null value when nullable is false"); + } + + // writes {value_flag} to "1" which means "the value is null" + context.writeValueFlagNull(buffer); + return; + } + + if (nullable) { + // writes {value_flag} to "0" which means "value is not null" + context.writeValueFlagNone(buffer); + } + + // sub classes will be responsible for writing {value_length} and {value_bytes} + writeCustomValue(value, buffer, context); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/ComplexTypeSerializerUtil.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/ComplexTypeSerializerUtil.java new file mode 100644 index 00000000000..bec3c78743a --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/ComplexTypeSerializerUtil.java @@ -0,0 +1,152 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph.binary; + +import com.datastax.dse.driver.internal.core.graph.TinkerpopBufferUtil; +import com.datastax.dse.driver.internal.core.graph.binary.buffer.DseNettyBufferFactory; +import com.datastax.dse.driver.internal.core.protocol.TinkerpopBufferPrimitiveCodec; +import com.datastax.oss.driver.api.core.data.GettableByIndex; +import com.datastax.oss.driver.api.core.data.SettableByIndex; +import com.datastax.oss.driver.api.core.type.CustomType; +import com.datastax.oss.driver.api.core.type.DataType; +import com.datastax.oss.driver.api.core.type.ListType; +import com.datastax.oss.driver.api.core.type.MapType; +import com.datastax.oss.driver.api.core.type.SetType; +import com.datastax.oss.driver.api.core.type.TupleType; +import com.datastax.oss.driver.api.core.type.UserDefinedType; +import com.datastax.oss.driver.internal.core.context.DefaultDriverContext; +import com.datastax.oss.driver.internal.core.type.DataTypeHelper; +import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableList; +import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableMap; +import com.datastax.oss.protocol.internal.PrimitiveCodec; +import com.datastax.oss.protocol.internal.ProtocolConstants; +import com.datastax.oss.protocol.internal.response.result.RawType; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.util.Objects; +import org.apache.tinkerpop.gremlin.structure.io.Buffer; + +class ComplexTypeSerializerUtil { + + private static final PrimitiveCodec codec = + new TinkerpopBufferPrimitiveCodec(new DseNettyBufferFactory()); + + static void encodeTypeDefinition( + DataType type, Buffer buffer, DefaultDriverContext driverContext) { + RawType protocolType = toProtocolSpec(type); + protocolType.encode(buffer, codec, driverContext.getProtocolVersion().getCode()); + } + + static DataType decodeTypeDefinition(Buffer buffer, DefaultDriverContext driverContext) { + RawType type = RawType.decode(buffer, codec, driverContext.getProtocolVersion().getCode()); + return DataTypeHelper.fromProtocolSpec(type, driverContext); + } + + /* Tinkerpop-based encoding of UDT values, based on the UdtCoded.encode() method, but using Tinkerpop buffers directly to avoid + unnecessary NIO ByteBuffer copies. */ + static void encodeValue(@Nullable GettableByIndex value, Buffer tinkerBuff) { + if (value == null) { + return; + } + + for (int i = 0; i < value.size(); i++) { + ByteBuffer fieldBuffer = value.getBytesUnsafe(i); + if (fieldBuffer == null) { + tinkerBuff.writeInt(-1); + } else { + tinkerBuff.writeInt(fieldBuffer.remaining()); + tinkerBuff.writeBytes(fieldBuffer.duplicate()); + } + } + } + + /* This method will move forward the Tinkerpop buffer given in parameter based on the UDT value read. + Content of the method is roughly equivalent to UdtCodec.decode(), but using Tinkerpop buffers directly to avoid + unnecessary NIO ByteBuffer copies. */ + static > T decodeValue(Buffer tinkerBuff, T val, int size) { + try { + for (int i = 0; i < size; i++) { + int fieldSize = tinkerBuff.readInt(); + if (fieldSize >= 0) { + // the reassignment is to shut down the error-prone warning about ignoring return values. + val = val.setBytesUnsafe(i, TinkerpopBufferUtil.readBytes(tinkerBuff, fieldSize)); + } + } + return val; + } catch (BufferUnderflowException e) { + throw new IllegalArgumentException("Not enough bytes to deserialize a UDT value", e); + } + } + + private static RawType toProtocolSpec(DataType dataType) { + int id = dataType.getProtocolCode(); + RawType type = RawType.PRIMITIVES.get(id); + if (type != null) { + return type; + } + + switch (id) { + case ProtocolConstants.DataType.CUSTOM: + CustomType customType = ((CustomType) dataType); + type = new RawType.RawCustom(customType.getClassName()); + break; + case ProtocolConstants.DataType.LIST: + ListType listType = ((ListType) dataType); + type = new RawType.RawList(toProtocolSpec(listType.getElementType())); + break; + case ProtocolConstants.DataType.SET: + SetType setType = ((SetType) dataType); + type = new RawType.RawSet(toProtocolSpec(setType.getElementType())); + break; + case ProtocolConstants.DataType.MAP: + MapType mapType = ((MapType) dataType); + type = + new RawType.RawMap( + toProtocolSpec(mapType.getKeyType()), toProtocolSpec(mapType.getValueType())); + break; + case ProtocolConstants.DataType.TUPLE: + TupleType tupleType = ((TupleType) dataType); + ImmutableList.Builder subTypesList = + ImmutableList.builderWithExpectedSize(tupleType.getComponentTypes().size()); + for (int i = 0; i < tupleType.getComponentTypes().size(); i++) { + subTypesList.add(toProtocolSpec(tupleType.getComponentTypes().get(i))); + } + type = new RawType.RawTuple(subTypesList.build()); + break; + case ProtocolConstants.DataType.UDT: + UserDefinedType userDefinedType = ((UserDefinedType) dataType); + ImmutableMap.Builder subTypesMap = + ImmutableMap.builderWithExpectedSize(userDefinedType.getFieldNames().size()); + for (int i = 0; i < userDefinedType.getFieldTypes().size(); i++) { + subTypesMap.put( + userDefinedType.getFieldNames().get(i).asInternal(), + toProtocolSpec(userDefinedType.getFieldTypes().get(i))); + } + type = + new RawType.RawUdt( + Objects.requireNonNull(userDefinedType.getKeyspace()).asInternal(), + userDefinedType.getName().asInternal(), + subTypesMap.build()); + break; + default: + throw new IllegalArgumentException("Unsupported type: " + dataType.asCql(true, true)); + } + return type; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/CqlDurationSerializer.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/CqlDurationSerializer.java new file mode 100644 index 00000000000..1ac97de0ef4 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/CqlDurationSerializer.java @@ -0,0 +1,52 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph.binary; + +import com.datastax.oss.driver.api.core.data.CqlDuration; +import java.io.IOException; +import org.apache.tinkerpop.gremlin.structure.io.Buffer; +import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryReader; +import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryWriter; + +public class CqlDurationSerializer extends AbstractSimpleGraphBinaryCustomSerializer { + + @Override + public String getTypeName() { + return GraphBinaryModule.GRAPH_BINARY_DURATION_TYPE_NAME; + } + + @Override + protected CqlDuration readCustomValue( + final int valueLength, final Buffer buffer, final GraphBinaryReader context) + throws IOException { + checkValueSize(GraphBinaryUtils.sizeOfDuration(), valueLength); + return CqlDuration.newInstance( + context.readValue(buffer, Integer.class, false), + context.readValue(buffer, Integer.class, false), + context.readValue(buffer, Long.class, false)); + } + + @Override + protected void writeCustomValue(CqlDuration value, Buffer buffer, GraphBinaryWriter context) + throws IOException { + context.writeValue(GraphBinaryUtils.sizeOfDuration(), buffer, false); + context.writeValue(value.getMonths(), buffer, false); + context.writeValue(value.getDays(), buffer, false); + context.writeValue(value.getNanoseconds(), buffer, false); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/DistanceSerializer.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/DistanceSerializer.java new file mode 100644 index 00000000000..9e281b2b84a --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/DistanceSerializer.java @@ -0,0 +1,48 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph.binary; + +import com.datastax.dse.driver.api.core.data.geometry.Point; +import com.datastax.dse.driver.internal.core.data.geometry.Distance; +import java.io.IOException; +import org.apache.tinkerpop.gremlin.structure.io.Buffer; +import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryReader; +import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryWriter; + +public class DistanceSerializer extends AbstractSimpleGraphBinaryCustomSerializer { + @Override + public String getTypeName() { + return GraphBinaryModule.GRAPH_BINARY_DISTANCE_TYPE_NAME; + } + + @Override + protected Distance readCustomValue(int valueLength, Buffer buffer, GraphBinaryReader context) + throws IOException { + Point p = context.readValue(buffer, Point.class, false); + checkValueSize(GraphBinaryUtils.sizeOfDistance(p), valueLength); + return new Distance(p, context.readValue(buffer, Double.class, false)); + } + + @Override + protected void writeCustomValue(Distance value, Buffer buffer, GraphBinaryWriter context) + throws IOException { + context.writeValue(GraphBinaryUtils.sizeOfDistance(value.getCenter()), buffer, false); + context.writeValue(value.getCenter(), buffer, false); + context.writeValue(value.getRadius(), buffer, false); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/EditDistanceSerializer.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/EditDistanceSerializer.java new file mode 100644 index 00000000000..b2831040123 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/EditDistanceSerializer.java @@ -0,0 +1,50 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph.binary; + +import com.datastax.dse.driver.internal.core.graph.EditDistance; +import java.io.IOException; +import org.apache.tinkerpop.gremlin.structure.io.Buffer; +import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryReader; +import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryWriter; + +public class EditDistanceSerializer + extends AbstractSimpleGraphBinaryCustomSerializer { + @Override + public String getTypeName() { + return GraphBinaryModule.GRAPH_BINARY_EDIT_DISTANCE_TYPE_NAME; + } + + @Override + protected EditDistance readCustomValue(int valueLength, Buffer buffer, GraphBinaryReader context) + throws IOException { + int distance = context.readValue(buffer, Integer.class, false); + String query = context.readValue(buffer, String.class, false); + checkValueSize(GraphBinaryUtils.sizeOfEditDistance(query), valueLength); + + return new EditDistance(query, distance); + } + + @Override + protected void writeCustomValue(EditDistance value, Buffer buffer, GraphBinaryWriter context) + throws IOException { + context.writeValue(GraphBinaryUtils.sizeOfEditDistance(value.query), buffer, false); + context.writeValue(value.distance, buffer, false); + context.writeValue(value.query, buffer, false); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/GeometrySerializer.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/GeometrySerializer.java new file mode 100644 index 00000000000..996e79c7693 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/GeometrySerializer.java @@ -0,0 +1,47 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph.binary; + +import com.datastax.dse.driver.api.core.data.geometry.Geometry; +import com.datastax.dse.driver.internal.core.graph.TinkerpopBufferUtil; +import java.io.IOException; +import java.nio.ByteBuffer; +import org.apache.tinkerpop.gremlin.structure.io.Buffer; +import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryReader; +import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryWriter; + +public abstract class GeometrySerializer + extends AbstractSimpleGraphBinaryCustomSerializer { + public abstract T fromWellKnownBinary(ByteBuffer buffer); + + @Override + protected T readCustomValue(int valueLength, Buffer buffer, GraphBinaryReader context) + throws IOException { + return fromWellKnownBinary(TinkerpopBufferUtil.readBytes(buffer, valueLength)); + } + + @Override + protected void writeCustomValue(T value, Buffer buffer, GraphBinaryWriter context) + throws IOException { + ByteBuffer bb = value.asWellKnownBinary(); + + // writing the {value_length} + context.writeValue(bb.remaining(), buffer, false); + buffer.writeBytes(bb); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/GraphBinaryModule.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/GraphBinaryModule.java new file mode 100644 index 00000000000..59f966a34c2 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/GraphBinaryModule.java @@ -0,0 +1,95 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph.binary; + +import com.datastax.dse.driver.api.core.data.geometry.LineString; +import com.datastax.dse.driver.api.core.data.geometry.Point; +import com.datastax.dse.driver.api.core.data.geometry.Polygon; +import com.datastax.dse.driver.internal.core.data.geometry.Distance; +import com.datastax.dse.driver.internal.core.graph.EditDistance; +import com.datastax.dse.driver.internal.core.graph.binary.buffer.DseNettyBufferFactory; +import com.datastax.oss.driver.api.core.data.CqlDuration; +import com.datastax.oss.driver.api.core.data.TupleValue; +import com.datastax.oss.driver.api.core.data.UdtValue; +import com.datastax.oss.driver.internal.core.context.DefaultDriverContext; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.UnpooledByteBufAllocator; +import java.io.IOException; +import org.apache.tinkerpop.gremlin.structure.io.Buffer; +import org.apache.tinkerpop.gremlin.structure.io.BufferFactory; +import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryReader; +import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryWriter; +import org.apache.tinkerpop.gremlin.structure.io.binary.TypeSerializerRegistry; +import org.javatuples.Pair; + +public class GraphBinaryModule { + public static final UnpooledByteBufAllocator ALLOCATOR = new UnpooledByteBufAllocator(false); + private static final BufferFactory FACTORY = new DseNettyBufferFactory(); + + static final String GRAPH_BINARY_POINT_TYPE_NAME = "driver.dse.geometry.Point"; + static final String GRAPH_BINARY_LINESTRING_TYPE_NAME = "driver.dse.geometry.LineString"; + static final String GRAPH_BINARY_POLYGON_TYPE_NAME = "driver.dse.geometry.Polygon"; + static final String GRAPH_BINARY_DISTANCE_TYPE_NAME = "driver.dse.geometry.Distance"; + static final String GRAPH_BINARY_DURATION_TYPE_NAME = "driver.core.Duration"; + static final String GRAPH_BINARY_EDIT_DISTANCE_TYPE_NAME = "driver.dse.search.EditDistance"; + static final String GRAPH_BINARY_TUPLE_VALUE_TYPE_NAME = "driver.core.TupleValue"; + static final String GRAPH_BINARY_UDT_VALUE_TYPE_NAME = "driver.core.UDTValue"; + static final String GRAPH_BINARY_PAIR_TYPE_NAME = "org.javatuples.Pair"; + + private final GraphBinaryReader reader; + private final GraphBinaryWriter writer; + + public GraphBinaryModule(GraphBinaryReader reader, GraphBinaryWriter writer) { + this.reader = reader; + this.writer = writer; + } + + public static TypeSerializerRegistry createDseTypeSerializerRegistry( + DefaultDriverContext driverContext) { + return TypeSerializerRegistry.build() + .addCustomType(CqlDuration.class, new CqlDurationSerializer()) + .addCustomType(Point.class, new PointSerializer()) + .addCustomType(LineString.class, new LineStringSerializer()) + .addCustomType(Polygon.class, new PolygonSerializer()) + .addCustomType(Distance.class, new DistanceSerializer()) + .addCustomType(EditDistance.class, new EditDistanceSerializer()) + .addCustomType(TupleValue.class, new TupleValueSerializer(driverContext)) + .addCustomType(UdtValue.class, new UdtValueSerializer(driverContext)) + .addCustomType(Pair.class, new PairSerializer()) + .create(); + } + + @SuppressWarnings("TypeParameterUnusedInFormals") + public T deserialize(final Buffer buffer) throws IOException { + return reader.read(buffer); + } + + public Buffer serialize(final T value) throws IOException { + return serialize(value, FACTORY.create(ALLOCATOR.heapBuffer())); + } + + public Buffer serialize(final T value, final Buffer buffer) throws IOException { + try { + writer.write(value, buffer); + return buffer; + } catch (Exception e) { + buffer.release(); + throw e; + } + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/GraphBinaryUtils.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/GraphBinaryUtils.java new file mode 100644 index 00000000000..42283cd5167 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/GraphBinaryUtils.java @@ -0,0 +1,57 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph.binary; + +import com.datastax.dse.driver.api.core.data.geometry.Point; +import java.nio.charset.StandardCharsets; + +class GraphBinaryUtils { + static int sizeOfInt() { + return 4; + } + + static int sizeOfLong() { + return 8; + } + + static int sizeOfDouble() { + return 8; + } + + static int sizeOfPoint(Point point) { + return point.asWellKnownBinary().remaining(); + } + + /* assumes UTF8 */ + static int sizeOfString(String s) { + // length + data length + return sizeOfInt() + s.getBytes(StandardCharsets.UTF_8).length; + } + + static int sizeOfDuration() { + return sizeOfInt() + sizeOfInt() + sizeOfLong(); + } + + static int sizeOfDistance(Point point) { + return sizeOfPoint(point) + sizeOfDouble(); + } + + static int sizeOfEditDistance(String s) { + return sizeOfInt() + sizeOfString(s); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/LineStringSerializer.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/LineStringSerializer.java new file mode 100644 index 00000000000..4dfa8f8f0f1 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/LineStringSerializer.java @@ -0,0 +1,33 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph.binary; + +import com.datastax.dse.driver.api.core.data.geometry.LineString; +import java.nio.ByteBuffer; + +public class LineStringSerializer extends GeometrySerializer { + @Override + public String getTypeName() { + return GraphBinaryModule.GRAPH_BINARY_LINESTRING_TYPE_NAME; + } + + @Override + public LineString fromWellKnownBinary(ByteBuffer buffer) { + return LineString.fromWellKnownBinary(buffer); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/PairSerializer.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/PairSerializer.java new file mode 100644 index 00000000000..3f13dd5b3a0 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/PairSerializer.java @@ -0,0 +1,45 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph.binary; + +import java.io.IOException; +import org.apache.tinkerpop.gremlin.structure.io.Buffer; +import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryReader; +import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryWriter; +import org.javatuples.Pair; + +public class PairSerializer extends AbstractDynamicGraphBinaryCustomSerializer { + + @Override + public String getTypeName() { + return GraphBinaryModule.GRAPH_BINARY_PAIR_TYPE_NAME; + } + + @Override + protected Pair readDynamicCustomValue(Buffer buffer, GraphBinaryReader context) + throws IOException { + return new Pair<>(context.read(buffer), context.read(buffer)); + } + + @Override + protected void writeDynamicCustomValue(Pair value, Buffer buffer, GraphBinaryWriter context) + throws IOException { + context.write(value.getValue0(), buffer); + context.write(value.getValue1(), buffer); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/PointSerializer.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/PointSerializer.java new file mode 100644 index 00000000000..2204b0da073 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/PointSerializer.java @@ -0,0 +1,34 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph.binary; + +import com.datastax.dse.driver.api.core.data.geometry.Point; +import java.nio.ByteBuffer; + +public class PointSerializer extends GeometrySerializer { + + @Override + public String getTypeName() { + return GraphBinaryModule.GRAPH_BINARY_POINT_TYPE_NAME; + } + + @Override + public Point fromWellKnownBinary(ByteBuffer buffer) { + return Point.fromWellKnownBinary(buffer); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/PolygonSerializer.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/PolygonSerializer.java new file mode 100644 index 00000000000..8e3bc67838a --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/PolygonSerializer.java @@ -0,0 +1,33 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph.binary; + +import com.datastax.dse.driver.api.core.data.geometry.Polygon; +import java.nio.ByteBuffer; + +public class PolygonSerializer extends GeometrySerializer { + @Override + public String getTypeName() { + return GraphBinaryModule.GRAPH_BINARY_POLYGON_TYPE_NAME; + } + + @Override + public Polygon fromWellKnownBinary(ByteBuffer buffer) { + return Polygon.fromWellKnownBinary(buffer); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/TupleValueSerializer.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/TupleValueSerializer.java new file mode 100644 index 00000000000..b7c6fc2098d --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/TupleValueSerializer.java @@ -0,0 +1,68 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph.binary; + +import com.datastax.oss.driver.api.core.data.TupleValue; +import com.datastax.oss.driver.api.core.type.DataType; +import com.datastax.oss.driver.api.core.type.TupleType; +import com.datastax.oss.driver.internal.core.context.DefaultDriverContext; +import java.io.IOException; +import org.apache.tinkerpop.gremlin.structure.io.Buffer; +import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryReader; +import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryWriter; + +public class TupleValueSerializer extends AbstractDynamicGraphBinaryCustomSerializer { + + private final DefaultDriverContext driverContext; + + public TupleValueSerializer(DefaultDriverContext driverContext) { + this.driverContext = driverContext; + } + + @Override + public String getTypeName() { + return GraphBinaryModule.GRAPH_BINARY_TUPLE_VALUE_TYPE_NAME; + } + + @Override + public TupleValue readDynamicCustomValue(Buffer buffer, GraphBinaryReader context) + throws IOException { + // read the type first + DataType type = ComplexTypeSerializerUtil.decodeTypeDefinition(buffer, driverContext); + + assert type instanceof TupleType + : "GraphBinary TupleValue deserializer was called on a value that is not encoded as a TupleValue."; + + TupleType tupleType = (TupleType) type; + TupleValue value = tupleType.newValue(); + + // then decode the values from the buffer + return ComplexTypeSerializerUtil.decodeValue( + buffer, value, tupleType.getComponentTypes().size()); + } + + @Override + public void writeDynamicCustomValue(TupleValue value, Buffer buffer, GraphBinaryWriter context) + throws IOException { + // write type first in native protocol + ComplexTypeSerializerUtil.encodeTypeDefinition(value.getType(), buffer, driverContext); + + // write value after + ComplexTypeSerializerUtil.encodeValue(value, buffer); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/UdtValueSerializer.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/UdtValueSerializer.java new file mode 100644 index 00000000000..3e617ebf926 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/UdtValueSerializer.java @@ -0,0 +1,66 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph.binary; + +import com.datastax.oss.driver.api.core.data.UdtValue; +import com.datastax.oss.driver.api.core.type.DataType; +import com.datastax.oss.driver.api.core.type.UserDefinedType; +import com.datastax.oss.driver.internal.core.context.DefaultDriverContext; +import java.io.IOException; +import org.apache.tinkerpop.gremlin.structure.io.Buffer; +import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryReader; +import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryWriter; + +public class UdtValueSerializer extends AbstractDynamicGraphBinaryCustomSerializer { + private final DefaultDriverContext driverContext; + + public UdtValueSerializer(DefaultDriverContext driverContext) { + this.driverContext = driverContext; + } + + @Override + public String getTypeName() { + return GraphBinaryModule.GRAPH_BINARY_UDT_VALUE_TYPE_NAME; + } + + @Override + public UdtValue readDynamicCustomValue(Buffer buffer, GraphBinaryReader context) + throws IOException { + // read type definition first + DataType driverType = ComplexTypeSerializerUtil.decodeTypeDefinition(buffer, driverContext); + + assert driverType instanceof UserDefinedType + : "GraphBinary UdtValue deserializer was called on a value that is not encoded as a UdtValue."; + + UserDefinedType userDefinedType = (UserDefinedType) driverType; + UdtValue value = userDefinedType.newValue(); + + // then read values + return ComplexTypeSerializerUtil.decodeValue( + buffer, value, userDefinedType.getFieldTypes().size()); + } + + @Override + public void writeDynamicCustomValue(UdtValue value, Buffer buffer, GraphBinaryWriter context) + throws IOException { + // write type first in native protocol format + ComplexTypeSerializerUtil.encodeTypeDefinition(value.getType(), buffer, driverContext); + // write value after + ComplexTypeSerializerUtil.encodeValue(value, buffer); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/buffer/DseNettyBuffer.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/buffer/DseNettyBuffer.java new file mode 100644 index 00000000000..590ac2e9be2 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/buffer/DseNettyBuffer.java @@ -0,0 +1,267 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph.binary.buffer; + +import io.netty.buffer.ByteBuf; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import org.apache.tinkerpop.gremlin.structure.io.Buffer; + +/** + * Internal impl of Tinkerpop Buffers. We implement an internal type here to allow for this class to + * use shaded Netty types (without bringing all of Tinkerpop into the shaded JAR). The impl is based + * on the initial impl of {@link NettyBuffer} but we don't guarantee that this class will mirror + * changes to that class over time. + */ +final class DseNettyBuffer implements Buffer { + private final ByteBuf buffer; + + /** + * Creates a new instance. + * + * @param buffer The buffer to wrap. + */ + DseNettyBuffer(ByteBuf buffer) { + if (buffer == null) { + throw new IllegalArgumentException("buffer can't be null"); + } + + this.buffer = buffer; + } + + @Override + public int readableBytes() { + return this.buffer.readableBytes(); + } + + @Override + public int readerIndex() { + return this.buffer.readerIndex(); + } + + @Override + public Buffer readerIndex(final int readerIndex) { + this.buffer.readerIndex(readerIndex); + return this; + } + + @Override + public int writerIndex() { + return this.buffer.writerIndex(); + } + + @Override + public Buffer writerIndex(final int writerIndex) { + this.buffer.writerIndex(writerIndex); + return this; + } + + @Override + public Buffer markWriterIndex() { + this.buffer.markWriterIndex(); + return this; + } + + @Override + public Buffer resetWriterIndex() { + this.buffer.resetWriterIndex(); + return this; + } + + @Override + public int capacity() { + return this.buffer.capacity(); + } + + @Override + public boolean isDirect() { + return this.buffer.isDirect(); + } + + @Override + public boolean readBoolean() { + return this.buffer.readBoolean(); + } + + @Override + public byte readByte() { + return this.buffer.readByte(); + } + + @Override + public short readShort() { + return this.buffer.readShort(); + } + + @Override + public int readInt() { + return this.buffer.readInt(); + } + + @Override + public long readLong() { + return this.buffer.readLong(); + } + + @Override + public float readFloat() { + return this.buffer.readFloat(); + } + + @Override + public double readDouble() { + return this.buffer.readDouble(); + } + + @Override + public Buffer readBytes(final byte[] destination) { + this.buffer.readBytes(destination); + return this; + } + + @Override + public Buffer readBytes(final byte[] destination, final int dstIndex, final int length) { + this.buffer.readBytes(destination, dstIndex, length); + return this; + } + + @Override + public Buffer readBytes(final ByteBuffer dst) { + this.buffer.readBytes(dst); + return this; + } + + @Override + public Buffer readBytes(final OutputStream out, final int length) throws IOException { + this.buffer.readBytes(out, length); + return this; + } + + @Override + public Buffer writeBoolean(final boolean value) { + this.buffer.writeBoolean(value); + return this; + } + + @Override + public Buffer writeByte(final int value) { + this.buffer.writeByte(value); + return this; + } + + @Override + public Buffer writeShort(final int value) { + this.buffer.writeShort(value); + return this; + } + + @Override + public Buffer writeInt(final int value) { + this.buffer.writeInt(value); + return this; + } + + @Override + public Buffer writeLong(final long value) { + this.buffer.writeLong(value); + return this; + } + + @Override + public Buffer writeFloat(final float value) { + this.buffer.writeFloat(value); + return this; + } + + @Override + public Buffer writeDouble(final double value) { + this.buffer.writeDouble(value); + return this; + } + + @Override + public Buffer writeBytes(final byte[] src) { + this.buffer.writeBytes(src); + return this; + } + + @Override + public Buffer writeBytes(final ByteBuffer src) { + this.buffer.writeBytes(src); + return this; + } + + @Override + public Buffer writeBytes(byte[] src, final int srcIndex, final int length) { + this.buffer.writeBytes(src, srcIndex, length); + return this; + } + + @Override + public boolean release() { + return this.buffer.release(); + } + + @Override + public Buffer retain() { + this.buffer.retain(); + return this; + } + + @Override + public int referenceCount() { + return this.buffer.refCnt(); + } + + @Override + public ByteBuffer[] nioBuffers() { + return this.buffer.nioBuffers(); + } + + @Override + public ByteBuffer nioBuffer() { + return this.buffer.nioBuffer(); + } + + @Override + public ByteBuffer nioBuffer(final int index, final int length) { + return this.buffer.nioBuffer(index, length); + } + + @Override + public ByteBuffer[] nioBuffers(final int index, final int length) { + return this.buffer.nioBuffers(index, length); + } + + @Override + public int nioBufferCount() { + return this.buffer.nioBufferCount(); + } + + @Override + public Buffer getBytes(final int index, final byte[] dst) { + this.buffer.getBytes(index, dst); + return this; + } + + /** Returns the underlying buffer. */ + public ByteBuf getUnderlyingBuffer() { + return this.buffer; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/buffer/DseNettyBufferFactory.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/buffer/DseNettyBufferFactory.java new file mode 100644 index 00000000000..57ee3cb1a9d --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/binary/buffer/DseNettyBufferFactory.java @@ -0,0 +1,126 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph.binary.buffer; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; +import io.netty.buffer.CompositeByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.buffer.UnpooledByteBufAllocator; +import java.nio.ByteBuffer; +import java.util.function.Supplier; +import org.apache.tinkerpop.gremlin.structure.io.Buffer; +import org.apache.tinkerpop.gremlin.structure.io.BufferFactory; + +/** + * Internal BufferFactory impl for creation of Tinkerpop buffers. We implement an internal type here + * to allow for this class to use shaded Netty types (without bringing all of Tinkerpop into the + * shaded JAR). The impl is based on the initial impl of {@code + * org.apache.tinkerpop.gremlin.driver.ser.NettyBufferFactory} but we don't guarantee that this + * class will mirror changes to that class over time. + */ +public class DseNettyBufferFactory implements BufferFactory { + + private static final ByteBufAllocator DEFAULT_ALLOCATOR = new UnpooledByteBufAllocator(false); + + private final ByteBufAllocator allocator; + + public DseNettyBufferFactory() { + this.allocator = DEFAULT_ALLOCATOR; + } + + public DseNettyBufferFactory(ByteBufAllocator allocator) { + this.allocator = allocator; + } + + @Override + public Buffer create(final ByteBuf value) { + return new DseNettyBuffer(value); + } + + @Override + public Buffer wrap(final ByteBuffer value) { + return create(Unpooled.wrappedBuffer(value)); + } + + public Buffer heap() { + return create(allocator.heapBuffer()); + } + + public Buffer heap(int initialSize) { + return create(allocator.heapBuffer(initialSize)); + } + + public Buffer heap(int initialSize, int maxSize) { + return create(allocator.heapBuffer(initialSize, maxSize)); + } + + public Buffer io() { + return create(allocator.ioBuffer()); + } + + public Buffer io(int initialSize) { + return create(allocator.ioBuffer(initialSize)); + } + + public Buffer io(int initialSize, int maxSize) { + return create(allocator.ioBuffer(initialSize, maxSize)); + } + + public Buffer direct() { + return create(allocator.directBuffer()); + } + + public Buffer direct(int initialSize) { + return create(allocator.directBuffer(initialSize)); + } + + public Buffer direct(int initialSize, int maxSize) { + return create(allocator.directBuffer(initialSize, maxSize)); + } + + public Buffer composite(ByteBuf... components) { + + CompositeByteBuf buff = allocator.compositeBuffer(components.length); + buff.addComponents(components); + return create(buff); + } + + public Buffer composite(Buffer... components) { + ByteBuf[] nettyBufs = new ByteBuf[components.length]; + for (int i = 0; i < components.length; ++i) { + if (!(components[i] instanceof DseNettyBuffer)) { + throw new IllegalArgumentException("Can only concatenate DseNettyBuffer instances"); + } + nettyBufs[i] = ((DseNettyBuffer) components[i]).getUnderlyingBuffer(); + } + return composite(nettyBufs); + } + + public Buffer withBytes(int... bytes) { + return withBytes(this::heap, bytes); + } + + public Buffer withBytes(Supplier supplier, int... bytes) { + Buffer buff = supplier.get(); + for (int val : bytes) { + buff.writeByte(val); + } + return buff; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/reactive/DefaultReactiveGraphNode.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/reactive/DefaultReactiveGraphNode.java new file mode 100644 index 00000000000..fda0eed5333 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/reactive/DefaultReactiveGraphNode.java @@ -0,0 +1,205 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph.reactive; + +import com.datastax.dse.driver.api.core.graph.GraphNode; +import com.datastax.dse.driver.api.core.graph.reactive.ReactiveGraphNode; +import com.datastax.oss.driver.api.core.cql.ExecutionInfo; +import com.datastax.oss.driver.api.core.type.reflect.GenericType; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.List; +import java.util.Map; +import java.util.Set; +import net.jcip.annotations.NotThreadSafe; +import org.apache.tinkerpop.gremlin.process.traversal.Path; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Property; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.structure.VertexProperty; + +@NotThreadSafe +class DefaultReactiveGraphNode implements ReactiveGraphNode { + + private final GraphNode graphNode; + private final ExecutionInfo executionInfo; + + DefaultReactiveGraphNode(@NonNull GraphNode graphNode, @NonNull ExecutionInfo executionInfo) { + this.graphNode = graphNode; + this.executionInfo = executionInfo; + } + + @NonNull + @Override + public ExecutionInfo getExecutionInfo() { + return executionInfo; + } + + @Override + public boolean isNull() { + return graphNode.isNull(); + } + + @Override + public boolean isMap() { + return graphNode.isMap(); + } + + @Override + public Iterable keys() { + return graphNode.keys(); + } + + @Override + public GraphNode getByKey(Object key) { + return graphNode.getByKey(key); + } + + @Override + public Map asMap() { + return graphNode.asMap(); + } + + @Override + public boolean isList() { + return graphNode.isList(); + } + + @Override + public int size() { + return graphNode.size(); + } + + @Override + public GraphNode getByIndex(int index) { + return graphNode.getByIndex(index); + } + + @Override + public List asList() { + return graphNode.asList(); + } + + @Override + public boolean isValue() { + return graphNode.isValue(); + } + + @Override + public int asInt() { + return graphNode.asInt(); + } + + @Override + public boolean asBoolean() { + return graphNode.asBoolean(); + } + + @Override + public long asLong() { + return graphNode.asLong(); + } + + @Override + public double asDouble() { + return graphNode.asDouble(); + } + + @Override + public String asString() { + return graphNode.asString(); + } + + @Override + public ResultT as(Class clazz) { + return graphNode.as(clazz); + } + + @Override + public ResultT as(GenericType type) { + return graphNode.as(type); + } + + @Override + public boolean isVertex() { + return graphNode.isVertex(); + } + + @Override + public Vertex asVertex() { + return graphNode.asVertex(); + } + + @Override + public boolean isEdge() { + return graphNode.isEdge(); + } + + @Override + public Edge asEdge() { + return graphNode.asEdge(); + } + + @Override + public boolean isPath() { + return graphNode.isPath(); + } + + @Override + public Path asPath() { + return graphNode.asPath(); + } + + @Override + public boolean isProperty() { + return graphNode.isProperty(); + } + + @Override + public Property asProperty() { + return graphNode.asProperty(); + } + + @Override + public boolean isVertexProperty() { + return graphNode.isVertexProperty(); + } + + @Override + public VertexProperty asVertexProperty() { + return graphNode.asVertexProperty(); + } + + @Override + public boolean isSet() { + return graphNode.isSet(); + } + + @Override + public Set asSet() { + return graphNode.asSet(); + } + + @Override + public String toString() { + return "DefaultReactiveGraphNode{graphNode=" + + graphNode + + ", executionInfo=" + + executionInfo + + '}'; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/reactive/DefaultReactiveGraphResultSet.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/reactive/DefaultReactiveGraphResultSet.java new file mode 100644 index 00000000000..137e44e4d95 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/reactive/DefaultReactiveGraphResultSet.java @@ -0,0 +1,85 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph.reactive; + +import com.datastax.dse.driver.api.core.graph.AsyncGraphResultSet; +import com.datastax.dse.driver.api.core.graph.reactive.ReactiveGraphNode; +import com.datastax.dse.driver.api.core.graph.reactive.ReactiveGraphResultSet; +import com.datastax.dse.driver.internal.core.cql.reactive.EmptySubscription; +import com.datastax.dse.driver.internal.core.cql.reactive.SimpleUnicastProcessor; +import com.datastax.oss.driver.api.core.cql.ExecutionInfo; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.Objects; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.atomic.AtomicBoolean; +import net.jcip.annotations.ThreadSafe; +import org.reactivestreams.Publisher; +import org.reactivestreams.Subscriber; + +@ThreadSafe +public class DefaultReactiveGraphResultSet implements ReactiveGraphResultSet { + + private final Callable> firstPage; + + private final AtomicBoolean alreadySubscribed = new AtomicBoolean(false); + + private final SimpleUnicastProcessor executionInfosPublisher = + new SimpleUnicastProcessor<>(); + + public DefaultReactiveGraphResultSet(Callable> firstPage) { + this.firstPage = firstPage; + } + + @Override + public void subscribe(@NonNull Subscriber subscriber) { + // As per rule 1.9, we need to throw an NPE if subscriber is null + Objects.requireNonNull(subscriber, "Subscriber cannot be null"); + // As per rule 1.11, this publisher is allowed to support only one subscriber. + if (alreadySubscribed.compareAndSet(false, true)) { + ReactiveGraphResultSetSubscription subscription = + new ReactiveGraphResultSetSubscription(subscriber, executionInfosPublisher); + try { + subscriber.onSubscribe(subscription); + // must be done after onSubscribe + subscription.start(firstPage); + } catch (Throwable t) { + // As per rule 2.13: In the case that this rule is violated, + // any associated Subscription to the Subscriber MUST be considered as + // cancelled, and the caller MUST raise this error condition in a fashion + // that is adequate for the runtime environment. + subscription.doOnError( + new IllegalStateException( + subscriber + + " violated the Reactive Streams rule 2.13 by throwing an exception from onSubscribe.", + t)); + } + } else { + subscriber.onSubscribe(EmptySubscription.INSTANCE); + subscriber.onError( + new IllegalStateException("This publisher does not support multiple subscriptions")); + } + // As per 2.13, this method must return normally (i.e. not throw) + } + + @NonNull + @Override + public Publisher getExecutionInfos() { + return executionInfosPublisher; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/reactive/FailedReactiveGraphResultSet.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/reactive/FailedReactiveGraphResultSet.java new file mode 100644 index 00000000000..45bbd8c62b0 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/reactive/FailedReactiveGraphResultSet.java @@ -0,0 +1,39 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph.reactive; + +import com.datastax.dse.driver.api.core.graph.reactive.ReactiveGraphNode; +import com.datastax.dse.driver.api.core.graph.reactive.ReactiveGraphResultSet; +import com.datastax.dse.driver.internal.core.cql.reactive.FailedPublisher; +import com.datastax.oss.driver.api.core.cql.ExecutionInfo; +import edu.umd.cs.findbugs.annotations.NonNull; +import org.reactivestreams.Publisher; + +public class FailedReactiveGraphResultSet extends FailedPublisher + implements ReactiveGraphResultSet { + + public FailedReactiveGraphResultSet(Throwable error) { + super(error); + } + + @NonNull + @Override + public Publisher getExecutionInfos() { + return new FailedPublisher<>(error); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/reactive/ReactiveGraphRequestProcessor.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/reactive/ReactiveGraphRequestProcessor.java new file mode 100644 index 00000000000..ed2cd28926c --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/reactive/ReactiveGraphRequestProcessor.java @@ -0,0 +1,63 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph.reactive; + +import com.datastax.dse.driver.api.core.graph.GraphStatement; +import com.datastax.dse.driver.api.core.graph.reactive.ReactiveGraphResultSet; +import com.datastax.dse.driver.internal.core.graph.GraphRequestAsyncProcessor; +import com.datastax.oss.driver.api.core.session.Request; +import com.datastax.oss.driver.api.core.type.reflect.GenericType; +import com.datastax.oss.driver.internal.core.context.InternalDriverContext; +import com.datastax.oss.driver.internal.core.session.DefaultSession; +import com.datastax.oss.driver.internal.core.session.RequestProcessor; +import edu.umd.cs.findbugs.annotations.NonNull; +import net.jcip.annotations.ThreadSafe; + +@ThreadSafe +public class ReactiveGraphRequestProcessor + implements RequestProcessor, ReactiveGraphResultSet> { + + public static final GenericType REACTIVE_GRAPH_RESULT_SET = + GenericType.of(ReactiveGraphResultSet.class); + + private final GraphRequestAsyncProcessor asyncGraphProcessor; + + public ReactiveGraphRequestProcessor(@NonNull GraphRequestAsyncProcessor asyncGraphProcessor) { + this.asyncGraphProcessor = asyncGraphProcessor; + } + + @Override + public boolean canProcess(Request request, GenericType resultType) { + return request instanceof GraphStatement && resultType.equals(REACTIVE_GRAPH_RESULT_SET); + } + + @Override + public ReactiveGraphResultSet process( + GraphStatement request, + DefaultSession session, + InternalDriverContext context, + String sessionLogPrefix) { + return new DefaultReactiveGraphResultSet( + () -> asyncGraphProcessor.process(request, session, context, sessionLogPrefix)); + } + + @Override + public ReactiveGraphResultSet newFailure(RuntimeException error) { + return new FailedReactiveGraphResultSet(error); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/graph/reactive/ReactiveGraphResultSetSubscription.java b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/reactive/ReactiveGraphResultSetSubscription.java new file mode 100644 index 00000000000..c3234d74ebc --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/graph/reactive/ReactiveGraphResultSetSubscription.java @@ -0,0 +1,475 @@ +/* + * 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 com.datastax.dse.driver.internal.core.graph.reactive; + +import com.datastax.dse.driver.api.core.graph.AsyncGraphResultSet; +import com.datastax.dse.driver.api.core.graph.reactive.ReactiveGraphNode; +import com.datastax.dse.driver.internal.core.cql.reactive.ReactiveOperators; +import com.datastax.dse.driver.internal.core.util.concurrent.BoundedConcurrentQueue; +import com.datastax.oss.driver.api.core.cql.ExecutionInfo; +import com.datastax.oss.driver.internal.core.util.concurrent.CompletableFutures; +import com.datastax.oss.driver.shaded.guava.common.collect.Iterators; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.util.Collections; +import java.util.Iterator; +import java.util.Objects; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import net.jcip.annotations.ThreadSafe; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class is very similar to {@link + * com.datastax.dse.driver.internal.core.cql.reactive.ReactiveResultSetSubscription}. It exists + * merely because {@link AsyncGraphResultSet} is not a subtype of {@link + * com.datastax.oss.driver.api.core.AsyncPagingIterable} and thus it would be difficult to re-use + * ReactiveResultSetSubscription for graph result sets. + */ +@ThreadSafe +public class ReactiveGraphResultSetSubscription implements Subscription { + + private static final Logger LOG = + LoggerFactory.getLogger(ReactiveGraphResultSetSubscription.class); + + private static final int MAX_ENQUEUED_PAGES = 4; + + /** Tracks the number of items requested by the subscriber. */ + private final AtomicLong requested = new AtomicLong(0); + + /** The pages received so far, with a maximum of MAX_ENQUEUED_PAGES elements. */ + private final BoundedConcurrentQueue pages = + new BoundedConcurrentQueue<>(MAX_ENQUEUED_PAGES); + + /** + * Used to signal that a thread is currently draining, i.e., emitting items to the subscriber. + * When it is zero, that means there is no ongoing emission. This mechanism effectively serializes + * access to the drain() method, and also keeps track of missed attempts to enter it, since each + * thread that attempts to drain will increment this counter. + * + * @see #drain() + */ + private final AtomicInteger draining = new AtomicInteger(0); + + /** + * Waited upon by the driver and completed when the subscriber requests its first item. + * + *

Used to hold off emitting results until the subscriber issues its first request for items. + * Since this future is only completed from {@link #request(long)}, this effectively conditions + * the enqueueing of the first page to the reception of the subscriber's first request. + * + *

This mechanism avoids sending terminal signals before a request is made when the stream is + * empty. Note that as per 2.9, "a Subscriber MUST be prepared to receive an onComplete signal + * with or without a preceding Subscription.request(long n) call." However, the TCK considers it + * as unfair behavior. + * + * @see #start(Callable) + * @see #request(long) + */ + private final CompletableFuture firstSubscriberRequestArrived = new CompletableFuture<>(); + + /** non-final because it has to be de-referenced, see {@link #clear()}. */ + private volatile Subscriber mainSubscriber; + + private volatile Subscriber executionInfosSubscriber; + + /** + * Set to true when the subscription is cancelled, which happens when an error is encountered, + * when the result set is fully consumed and the subscription terminates, or when the subscriber + * manually calls {@link #cancel()}. + */ + private volatile boolean cancelled = false; + + ReactiveGraphResultSetSubscription( + @NonNull Subscriber mainSubscriber, + @NonNull Subscriber executionInfosSubscriber) { + this.mainSubscriber = mainSubscriber; + this.executionInfosSubscriber = executionInfosSubscriber; + } + + /** + * Starts the query execution. + * + *

Must be called immediately after creating the subscription, but after {@link + * Subscriber#onSubscribe(Subscription)}. + * + * @param firstPage The future that, when complete, will produce the first page. + */ + void start(@NonNull Callable> firstPage) { + firstSubscriberRequestArrived.thenAccept( + (aVoid) -> fetchNextPageAndEnqueue(new Page(firstPage))); + } + + @Override + public void request(long n) { + // As per 3.6: after the Subscription is cancelled, additional + // calls to request() MUST be NOPs. + if (!cancelled) { + if (n < 1) { + // Validate request as per rule 3.9 + doOnError( + new IllegalArgumentException( + mainSubscriber + + " violated the Reactive Streams rule 3.9 by requesting a non-positive number of elements.")); + } else { + // As per rule 3.17, when demand overflows Long.MAX_VALUE + // it can be treated as "effectively unbounded" + ReactiveOperators.addCap(requested, n); + // Set the first future to true if not done yet. + // This will make the first page of results ready for consumption, + // see start(). + // As per 2.7 it is the subscriber's responsibility to provide + // external synchronization when calling request(), + // so the check-then-act idiom below is good enough + // (and besides, complete() is idempotent). + if (!firstSubscriberRequestArrived.isDone()) { + firstSubscriberRequestArrived.complete(null); + } + drain(); + } + } + } + + @Override + public void cancel() { + // As per 3.5: Subscription.cancel() MUST respect the responsiveness of + // its caller by returning in a timely manner, MUST be idempotent and + // MUST be thread-safe. + if (!cancelled) { + cancelled = true; + if (draining.getAndIncrement() == 0) { + // If nobody is draining, clear now; + // otherwise, the draining thread will notice + // that the cancelled flag was set + // and will clear for us. + clear(); + } + } + } + + /** + * Attempts to drain available items, i.e. emit them to the subscriber. + * + *

Access to this method is serialized by the field {@link #draining}: only one thread at a + * time can drain, but threads that attempt to drain while other thread is already draining + * increment that field; the draining thread, before finishing its work, checks for such failed + * attempts and triggers another round of draining if that was the case. + * + *

The loop is interrupted when 1) the requested amount has been met or 2) when there are no + * more items readily available or 3) the subscription has been cancelled. + * + *

The loop also checks for stream exhaustion and emits a terminal {@code onComplete} signal in + * this case. + * + *

This method may run on a driver IO thread when invoked from {@link + * #fetchNextPageAndEnqueue(Page)}, or on a subscriber thread, when invoked from {@link + * #request(long)}. + */ + @SuppressWarnings("ConditionalBreakInInfiniteLoop") + private void drain() { + // As per 3.4: this method SHOULD respect the responsiveness + // of its caller by returning in a timely manner. + // We accomplish this by a wait-free implementation. + if (draining.getAndIncrement() != 0) { + // Someone else is already draining, so do nothing, + // the other thread will notice that we attempted to drain. + // This also allows to abide by rule 3.3 and avoid + // cycles such as request() -> onNext() -> request() etc. + return; + } + int missed = 1; + // Note: when termination is detected inside this loop, + // we MUST call clear() manually. + for (; ; ) { + // The requested number of items at this point + long r = requested.get(); + // The number of items emitted thus far + long emitted = 0L; + while (emitted != r) { + if (cancelled) { + clear(); + return; + } + Object result; + try { + result = tryNext(); + } catch (Throwable t) { + doOnError(t); + clear(); + return; + } + if (result == null) { + break; + } + if (result instanceof Throwable) { + doOnError((Throwable) result); + clear(); + return; + } + doOnNext((ReactiveGraphNode) result); + emitted++; + } + if (isExhausted()) { + doOnComplete(); + clear(); + return; + } + if (cancelled) { + clear(); + return; + } + if (emitted != 0) { + // if any item was emitted, adjust the requested field + ReactiveOperators.subCap(requested, emitted); + } + // if another thread tried to call drain() while we were busy, + // then we should do another drain round. + missed = draining.addAndGet(-missed); + if (missed == 0) { + break; + } + } + } + + /** + * Tries to return the next item, if one is readily available, and returns {@code null} otherwise. + * + *

Cannot run concurrently due to the {@link #draining} field. + */ + @Nullable + private Object tryNext() { + Page current = pages.peek(); + if (current != null) { + if (current.hasMoreRows()) { + return current.nextRow(); + } else if (current.hasMorePages()) { + // Discard current page as it is consumed. + // Don't discard the last page though as we need it + // to test isExhausted(). It will be GC'ed when a terminal signal + // is issued anyway, so that's no big deal. + if (pages.poll() == null) { + throw new AssertionError("Queue is empty, this should not happen"); + } + current = pages.peek(); + // if the next page is readily available, + // serve its first row now, no need to wait + // for the next drain. + if (current != null && current.hasMoreRows()) { + return current.nextRow(); + } + } + } + // No items available right now. + return null; + } + + /** + * Returns {@code true} when the entire stream has been consumed and no more items can be emitted. + * When that is the case, a terminal signal is sent. + * + *

Cannot run concurrently due to the draining field. + */ + private boolean isExhausted() { + Page current = pages.peek(); + // Note: current can only be null when: + // 1) we are waiting for the first page and it hasn't arrived yet; + // 2) we just discarded the current page, but the next page hasn't arrived yet. + // In any case, a null here means it is not the last page, since the last page + // stays in the queue until the very end of the operation. + return current != null && !current.hasMoreRows() && !current.hasMorePages(); + } + + /** + * Runs on a subscriber thread initially, see {@link #start(Callable)}. Subsequent executions run + * on the thread that completes the pair of futures [current.fetchNextPage, pages.offer] and + * enqueues. This can be a driver IO thread or a subscriber thread; in both cases, cannot run + * concurrently due to the fact that one can only fetch the next page when the current one is + * arrived and enqueued. + */ + private void fetchNextPageAndEnqueue(@NonNull Page current) { + current + .fetchNextPage() + // as soon as the response arrives, + // create the new page + .handle( + (rs, t) -> { + Page page; + if (t == null) { + page = toPage(rs); + executionInfosSubscriber.onNext(rs.getRequestExecutionInfo()); + if (!page.hasMorePages()) { + executionInfosSubscriber.onComplete(); + } + } else { + // Unwrap CompletionExceptions created by combined futures + if (t instanceof CompletionException) { + t = t.getCause(); + } + page = toErrorPage(t); + executionInfosSubscriber.onError(t); + } + return page; + }) + .thenCompose(pages::offer) + .thenAccept( + page -> { + if (page.hasMorePages() && !cancelled) { + // preemptively fetch the next page, if available + fetchNextPageAndEnqueue(page); + } + drain(); + }); + } + + private void doOnNext(@NonNull ReactiveGraphNode result) { + try { + mainSubscriber.onNext(result); + } catch (Throwable t) { + LOG.error( + mainSubscriber + + " violated the Reactive Streams rule 2.13 by throwing an exception from onNext.", + t); + cancel(); + } + } + + private void doOnComplete() { + try { + // Then we signal onComplete as per rules 1.2 and 1.5 + mainSubscriber.onComplete(); + } catch (Throwable t) { + LOG.error( + mainSubscriber + + " violated the Reactive Streams rule 2.13 by throwing an exception from onComplete.", + t); + } + // We need to consider this Subscription as cancelled as per rule 1.6 + cancel(); + } + + // package-private because it can be invoked by the publisher if the subscription handshake + // process fails. + void doOnError(@NonNull Throwable error) { + try { + // Then we signal the error downstream, as per rules 1.2 and 1.4. + mainSubscriber.onError(error); + } catch (Throwable t) { + t.addSuppressed(error); + LOG.error( + mainSubscriber + + " violated the Reactive Streams rule 2.13 by throwing an exception from onError.", + t); + } + // We need to consider this Subscription as cancelled as per rule 1.6 + cancel(); + } + + private void clear() { + // We don't need these pages anymore and should not hold references + // to them. + pages.clear(); + // As per 3.13, Subscription.cancel() MUST request the Publisher to + // eventually drop any references to the corresponding subscriber. + // Our own publishers do not keep references to this subscription, + // but downstream processors might do so, which is why we need to + // defensively clear the subscriber reference when we are done. + mainSubscriber = null; + executionInfosSubscriber = null; + } + + /** + * Converts the received result object into a {@link Page}. + * + * @param rs the result object to convert. + * @return a new page. + */ + @NonNull + private Page toPage(@NonNull AsyncGraphResultSet rs) { + ExecutionInfo executionInfo = rs.getRequestExecutionInfo(); + Iterator results = + Iterators.transform( + rs.currentPage().iterator(), + row -> new DefaultReactiveGraphNode(Objects.requireNonNull(row), executionInfo)); + return new Page(results, rs.hasMorePages() ? rs::fetchNextPage : null); + } + + /** Converts the given error into a {@link Page}, containing the error as its only element. */ + @NonNull + private Page toErrorPage(@NonNull Throwable t) { + return new Page(Iterators.singletonIterator(t), null); + } + + /** + * A page object comprises an iterator over the page's results, and a future pointing to the next + * page (or {@code null}, if it's the last page). + */ + static class Page { + + @NonNull final Iterator iterator; + + // A pointer to the next page, or null if this is the last page. + @Nullable final Callable> nextPage; + + /** called only from start() */ + Page(@NonNull Callable> nextPage) { + this.iterator = Collections.emptyIterator(); + this.nextPage = nextPage; + } + + Page( + @NonNull Iterator iterator, + @Nullable Callable> nextPage) { + this.iterator = iterator; + this.nextPage = nextPage; + } + + boolean hasMorePages() { + return nextPage != null; + } + + @NonNull + CompletionStage fetchNextPage() { + try { + return Objects.requireNonNull(nextPage).call(); + } catch (Exception e) { + // This is a synchronous failure in the driver. + // It can happen in rare cases when the driver throws an exception instead of returning a + // failed future; e.g. if someone tries to execute a continuous paging request but the + // protocol version in use does not support it. + // We treat it as a failed future. + return CompletableFutures.failedFuture(e); + } + } + + boolean hasMoreRows() { + return iterator.hasNext(); + } + + @NonNull + Object nextRow() { + return iterator.next(); + } + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/insights/AddressFormatter.java b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/AddressFormatter.java new file mode 100644 index 00000000000..cecc951a3ab --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/AddressFormatter.java @@ -0,0 +1,54 @@ +/* + * 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 com.datastax.dse.driver.internal.core.insights; + +import java.net.InetAddress; +import java.net.InetSocketAddress; + +class AddressFormatter { + + static String nullSafeToString(Object address) { + if (address instanceof InetAddress) { + return nullSafeToString((InetAddress) address); + } else if (address instanceof InetSocketAddress) { + return nullSafeToString((InetSocketAddress) address); + } else if (address instanceof String) { + return address.toString(); + } else { + return ""; + } + } + + static String nullSafeToString(InetAddress inetAddress) { + return inetAddress != null ? inetAddress.getHostAddress() : null; + } + + static String nullSafeToString(InetSocketAddress inetSocketAddress) { + if (inetSocketAddress != null) { + if (inetSocketAddress.isUnresolved()) { + return String.format( + "%s:%s", + nullSafeToString(inetSocketAddress.getHostName()), inetSocketAddress.getPort()); + } else { + return String.format( + "%s:%s", nullSafeToString(inetSocketAddress.getAddress()), inetSocketAddress.getPort()); + } + } + return null; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/insights/ConfigAntiPatternsFinder.java b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/ConfigAntiPatternsFinder.java new file mode 100644 index 00000000000..7f5b9c20a0e --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/ConfigAntiPatternsFinder.java @@ -0,0 +1,46 @@ +/* + * 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 com.datastax.dse.driver.internal.core.insights; + +import static com.datastax.oss.driver.api.core.config.DefaultDriverOption.SSL_ENGINE_FACTORY_CLASS; +import static com.datastax.oss.driver.api.core.config.DefaultDriverOption.SSL_HOSTNAME_VALIDATION; + +import com.datastax.oss.driver.internal.core.context.InternalDriverContext; +import java.util.HashMap; +import java.util.Map; + +class ConfigAntiPatternsFinder { + Map findAntiPatterns(InternalDriverContext driverContext) { + Map antiPatterns = new HashMap<>(); + findSslAntiPattern(driverContext, antiPatterns); + return antiPatterns; + } + + private void findSslAntiPattern( + InternalDriverContext driverContext, Map antiPatterns) { + boolean isSslDefined = + driverContext.getConfig().getDefaultProfile().isDefined(SSL_ENGINE_FACTORY_CLASS); + boolean certValidation = + driverContext.getConfig().getDefaultProfile().getBoolean(SSL_HOSTNAME_VALIDATION, false); + if (isSslDefined && !certValidation) { + antiPatterns.put( + "sslWithoutCertValidation", + "Client-to-node encryption is enabled but server certificate validation is disabled"); + } + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/insights/DataCentersFinder.java b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/DataCentersFinder.java new file mode 100644 index 00000000000..7112b8dcdf7 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/DataCentersFinder.java @@ -0,0 +1,55 @@ +/* + * 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 com.datastax.dse.driver.internal.core.insights; + +import static com.datastax.oss.driver.api.core.config.DefaultDriverOption.CONNECTION_POOL_REMOTE_SIZE; + +import com.datastax.oss.driver.api.core.config.DriverExecutionProfile; +import com.datastax.oss.driver.api.core.loadbalancing.NodeDistance; +import com.datastax.oss.driver.api.core.metadata.Node; +import com.datastax.oss.driver.internal.core.context.InternalDriverContext; +import com.datastax.oss.driver.shaded.guava.common.annotations.VisibleForTesting; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +class DataCentersFinder { + + Set getDataCenters(InternalDriverContext driverContext) { + return getDataCenters( + driverContext.getMetadataManager().getMetadata().getNodes().values(), + driverContext.getConfig().getDefaultProfile()); + } + + @VisibleForTesting + Set getDataCenters(Collection nodes, DriverExecutionProfile executionProfile) { + + int remoteConnectionsLength = executionProfile.getInt(CONNECTION_POOL_REMOTE_SIZE); + + Set dataCenters = new HashSet<>(); + for (Node n : nodes) { + NodeDistance distance = n.getDistance(); + + if (distance.equals(NodeDistance.LOCAL) + || (distance.equals(NodeDistance.REMOTE) && remoteConnectionsLength > 0)) { + dataCenters.add(n.getDatacenter()); + } + } + return dataCenters; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/insights/ExecutionProfilesInfoFinder.java b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/ExecutionProfilesInfoFinder.java new file mode 100644 index 00000000000..a7c92d80d96 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/ExecutionProfilesInfoFinder.java @@ -0,0 +1,184 @@ +/* + * 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 com.datastax.dse.driver.internal.core.insights; + +import static com.datastax.dse.driver.api.core.config.DseDriverOption.GRAPH_TRAVERSAL_SOURCE; + +import com.datastax.dse.driver.internal.core.insights.PackageUtil.ClassSettingDetails; +import com.datastax.dse.driver.internal.core.insights.schema.LoadBalancingInfo; +import com.datastax.dse.driver.internal.core.insights.schema.SpecificExecutionProfile; +import com.datastax.dse.driver.internal.core.insights.schema.SpeculativeExecutionInfo; +import com.datastax.oss.driver.api.core.config.DefaultDriverOption; +import com.datastax.oss.driver.api.core.config.DriverExecutionProfile; +import com.datastax.oss.driver.internal.core.context.InternalDriverContext; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +class ExecutionProfilesInfoFinder { + Map getExecutionProfilesInfo( + InternalDriverContext driverContext) { + + SpecificExecutionProfile defaultProfile = + mapToSpecificProfile(driverContext.getConfig().getDefaultProfile()); + + return driverContext.getConfig().getProfiles().entrySet().stream() + .collect( + Collectors.toMap( + Map.Entry::getKey, + e -> { + if (isNotDefaultProfile(e)) { + SpecificExecutionProfile specificExecutionProfile = + mapToSpecificProfile(e.getValue()); + return retainOnlyDifferentFieldsFromSpecificProfile( + defaultProfile, specificExecutionProfile); + } else { + return defaultProfile; + } + })); + } + + private boolean isNotDefaultProfile(Map.Entry e) { + return !e.getKey().equals("default"); + } + + private SpecificExecutionProfile retainOnlyDifferentFieldsFromSpecificProfile( + SpecificExecutionProfile defaultProfile, SpecificExecutionProfile specificExecutionProfile) { + Integer readTimeout = + getIfDifferentOrReturnNull( + defaultProfile, specificExecutionProfile, SpecificExecutionProfile::getReadTimeout); + LoadBalancingInfo loadBalancingInfo = + getIfDifferentOrReturnNull( + defaultProfile, specificExecutionProfile, SpecificExecutionProfile::getLoadBalancing); + + SpeculativeExecutionInfo speculativeExecutionInfo = + getIfDifferentOrReturnNull( + defaultProfile, + specificExecutionProfile, + SpecificExecutionProfile::getSpeculativeExecution); + + String consistency = + getIfDifferentOrReturnNull( + defaultProfile, specificExecutionProfile, SpecificExecutionProfile::getConsistency); + + String serialConsistency = + getIfDifferentOrReturnNull( + defaultProfile, + specificExecutionProfile, + SpecificExecutionProfile::getSerialConsistency); + + Map graphOptions = + getIfDifferentOrReturnNull( + defaultProfile, specificExecutionProfile, SpecificExecutionProfile::getGraphOptions); + + return new SpecificExecutionProfile( + readTimeout, + loadBalancingInfo, + speculativeExecutionInfo, + consistency, + serialConsistency, + graphOptions); + } + + private T getIfDifferentOrReturnNull( + SpecificExecutionProfile defaultProfile, + SpecificExecutionProfile profile, + Function valueExtractor) { + T defaultProfileValue = valueExtractor.apply(defaultProfile); + T specificProfileValue = valueExtractor.apply(profile); + if (defaultProfileValue.equals(specificProfileValue)) { + return null; + } else { + return specificProfileValue; + } + } + + private SpecificExecutionProfile mapToSpecificProfile( + DriverExecutionProfile driverExecutionProfile) { + return new SpecificExecutionProfile( + (int) driverExecutionProfile.getDuration(DefaultDriverOption.REQUEST_TIMEOUT).toMillis(), + getLoadBalancingInfo(driverExecutionProfile), + getSpeculativeExecutionInfo(driverExecutionProfile), + driverExecutionProfile.getString(DefaultDriverOption.REQUEST_CONSISTENCY), + driverExecutionProfile.getString(DefaultDriverOption.REQUEST_SERIAL_CONSISTENCY), + getGraphOptions(driverExecutionProfile)); + } + + private SpeculativeExecutionInfo getSpeculativeExecutionInfo( + DriverExecutionProfile driverExecutionProfile) { + Map options = new LinkedHashMap<>(); + + putIfExists( + options, + "maxSpeculativeExecutions", + DefaultDriverOption.SPECULATIVE_EXECUTION_MAX, + driverExecutionProfile); + putIfExists( + options, "delay", DefaultDriverOption.SPECULATIVE_EXECUTION_DELAY, driverExecutionProfile); + + ClassSettingDetails speculativeExecutionDetails = + PackageUtil.getSpeculativeExecutionDetails( + driverExecutionProfile.getString( + DefaultDriverOption.SPECULATIVE_EXECUTION_POLICY_CLASS)); + return new SpeculativeExecutionInfo( + speculativeExecutionDetails.getClassName(), + options, + speculativeExecutionDetails.getFullPackage()); + } + + private void putIfExists( + Map options, + String key, + DefaultDriverOption option, + DriverExecutionProfile executionProfile) { + if (executionProfile.isDefined(option)) { + options.put(key, executionProfile.getInt(option)); + } + } + + private LoadBalancingInfo getLoadBalancingInfo(DriverExecutionProfile driverExecutionProfile) { + Map options = new LinkedHashMap<>(); + if (driverExecutionProfile.isDefined(DefaultDriverOption.LOAD_BALANCING_LOCAL_DATACENTER)) { + options.put( + "localDataCenter", + driverExecutionProfile.getString(DefaultDriverOption.LOAD_BALANCING_LOCAL_DATACENTER)); + } + @SuppressWarnings("deprecation") + boolean hasNodeFiltering = + driverExecutionProfile.isDefined(DefaultDriverOption.LOAD_BALANCING_FILTER_CLASS) + || driverExecutionProfile.isDefined( + DefaultDriverOption.LOAD_BALANCING_DISTANCE_EVALUATOR_CLASS); + options.put("filterFunction", hasNodeFiltering); + ClassSettingDetails loadBalancingDetails = + PackageUtil.getLoadBalancingDetails( + driverExecutionProfile.getString(DefaultDriverOption.LOAD_BALANCING_POLICY_CLASS)); + return new LoadBalancingInfo( + loadBalancingDetails.getClassName(), options, loadBalancingDetails.getFullPackage()); + } + + private Map getGraphOptions(DriverExecutionProfile driverExecutionProfile) { + Map graphOptionsMap = new HashMap<>(); + String graphTraversalSource = driverExecutionProfile.getString(GRAPH_TRAVERSAL_SOURCE, null); + if (graphTraversalSource != null) { + graphOptionsMap.put("source", graphTraversalSource); + } + return graphOptionsMap; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/insights/InsightsClient.java b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/InsightsClient.java new file mode 100644 index 00000000000..f19687adf45 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/InsightsClient.java @@ -0,0 +1,491 @@ +/* + * 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 com.datastax.dse.driver.internal.core.insights; + +import static com.datastax.oss.driver.api.core.config.DefaultDriverOption.AUTH_PROVIDER_CLASS; +import static com.datastax.oss.driver.api.core.config.DefaultDriverOption.CONNECTION_POOL_LOCAL_SIZE; +import static com.datastax.oss.driver.api.core.config.DefaultDriverOption.CONNECTION_POOL_REMOTE_SIZE; +import static com.datastax.oss.driver.api.core.config.DefaultDriverOption.HEARTBEAT_INTERVAL; +import static com.datastax.oss.driver.api.core.config.DefaultDriverOption.PROTOCOL_COMPRESSION; +import static com.datastax.oss.driver.api.core.config.DefaultDriverOption.SSL_ENGINE_FACTORY_CLASS; +import static com.datastax.oss.driver.api.core.config.DefaultDriverOption.SSL_HOSTNAME_VALIDATION; + +import com.datastax.dse.driver.api.core.DseProtocolVersion; +import com.datastax.dse.driver.internal.core.insights.PackageUtil.ClassSettingDetails; +import com.datastax.dse.driver.internal.core.insights.configuration.InsightsConfiguration; +import com.datastax.dse.driver.internal.core.insights.exceptions.InsightEventFormatException; +import com.datastax.dse.driver.internal.core.insights.schema.AuthProviderType; +import com.datastax.dse.driver.internal.core.insights.schema.Insight; +import com.datastax.dse.driver.internal.core.insights.schema.InsightMetadata; +import com.datastax.dse.driver.internal.core.insights.schema.InsightType; +import com.datastax.dse.driver.internal.core.insights.schema.InsightsStartupData; +import com.datastax.dse.driver.internal.core.insights.schema.InsightsStatusData; +import com.datastax.dse.driver.internal.core.insights.schema.PoolSizeByHostDistance; +import com.datastax.dse.driver.internal.core.insights.schema.SSL; +import com.datastax.dse.driver.internal.core.insights.schema.SessionStateForNode; +import com.datastax.oss.driver.api.core.config.DefaultDriverOption; +import com.datastax.oss.driver.api.core.metadata.Node; +import com.datastax.oss.driver.api.core.session.SessionBuilder; +import com.datastax.oss.driver.api.core.type.DataTypes; +import com.datastax.oss.driver.api.core.type.codec.TypeCodec; +import com.datastax.oss.driver.api.core.uuid.Uuids; +import com.datastax.oss.driver.internal.core.adminrequest.AdminRequestHandler; +import com.datastax.oss.driver.internal.core.context.DefaultDriverContext; +import com.datastax.oss.driver.internal.core.context.InternalDriverContext; +import com.datastax.oss.driver.internal.core.context.StartupOptionsBuilder; +import com.datastax.oss.driver.internal.core.control.ControlConnection; +import com.datastax.oss.driver.internal.core.pool.ChannelPool; +import com.datastax.oss.driver.internal.core.util.concurrent.CompletableFutures; +import com.datastax.oss.driver.shaded.guava.common.annotations.VisibleForTesting; +import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableMap; +import com.datastax.oss.protocol.internal.request.Query; +import com.datastax.oss.protocol.internal.request.query.QueryOptions; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.net.UnknownHostException; +import java.nio.ByteBuffer; +import java.time.Duration; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class InsightsClient { + private static final Logger LOGGER = LoggerFactory.getLogger(InsightsClient.class); + private static final String STARTUP_MESSAGE_NAME = "driver.startup"; + private static final String STATUS_MESSAGE_NAME = "driver.status"; + private static final String REPORT_INSIGHT_RPC = "CALL InsightsRpc.reportInsight(?)"; + private static final Map TAGS = ImmutableMap.of("language", "java"); + private static final String STARTUP_VERSION_1_ID = "v1"; + private static final String STATUS_VERSION_1_ID = "v1"; + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + private static final int MAX_NUMBER_OF_STATUS_ERROR_LOGS = 5; + static final String DEFAULT_JAVA_APPLICATION = "Default Java Application"; + + private final ControlConnection controlConnection; + private final String id = Uuids.random().toString(); + private final InsightsConfiguration insightsConfiguration; + private final AtomicInteger numberOfStatusEventErrors = new AtomicInteger(); + + private final InternalDriverContext driverContext; + private final Supplier timestampSupplier; + private final PlatformInfoFinder platformInfoFinder; + private final ReconnectionPolicyInfoFinder reconnectionPolicyInfoInfoFinder; + private final ExecutionProfilesInfoFinder executionProfilesInfoFinder; + private final ConfigAntiPatternsFinder configAntiPatternsFinder; + private final DataCentersFinder dataCentersFinder; + private final StackTraceElement[] initCallStackTrace; + + private volatile ScheduledFuture scheduleInsightsTask; + + public static InsightsClient createInsightsClient( + InsightsConfiguration insightsConfiguration, + InternalDriverContext driverContext, + StackTraceElement[] initCallStackTrace) { + DataCentersFinder dataCentersFinder = new DataCentersFinder(); + return new InsightsClient( + driverContext, + () -> new Date().getTime(), + insightsConfiguration, + new PlatformInfoFinder(), + new ReconnectionPolicyInfoFinder(), + new ExecutionProfilesInfoFinder(), + new ConfigAntiPatternsFinder(), + dataCentersFinder, + initCallStackTrace); + } + + InsightsClient( + InternalDriverContext driverContext, + Supplier timestampSupplier, + InsightsConfiguration insightsConfiguration, + PlatformInfoFinder platformInfoFinder, + ReconnectionPolicyInfoFinder reconnectionPolicyInfoInfoFinder, + ExecutionProfilesInfoFinder executionProfilesInfoFinder, + ConfigAntiPatternsFinder configAntiPatternsFinder, + DataCentersFinder dataCentersFinder, + StackTraceElement[] initCallStackTrace) { + this.driverContext = driverContext; + this.controlConnection = driverContext.getControlConnection(); + this.timestampSupplier = timestampSupplier; + this.insightsConfiguration = insightsConfiguration; + this.platformInfoFinder = platformInfoFinder; + this.reconnectionPolicyInfoInfoFinder = reconnectionPolicyInfoInfoFinder; + this.executionProfilesInfoFinder = executionProfilesInfoFinder; + this.configAntiPatternsFinder = configAntiPatternsFinder; + this.dataCentersFinder = dataCentersFinder; + this.initCallStackTrace = initCallStackTrace; + } + + public CompletionStage sendStartupMessage() { + try { + if (!shouldSendEvent()) { + return CompletableFuture.completedFuture(null); + } else { + String startupMessage = createStartupMessage(); + return sendJsonMessage(startupMessage) + .whenComplete( + (aVoid, throwable) -> { + if (throwable != null) { + LOGGER.debug( + "Error while sending startup message to Insights. Message was: " + + trimToFirst500characters(startupMessage), + throwable); + } + }); + } + } catch (Exception e) { + LOGGER.debug("Unexpected error while sending startup message to Insights.", e); + return CompletableFutures.failedFuture(e); + } + } + + private static String trimToFirst500characters(String startupMessage) { + return startupMessage.substring(0, Math.min(startupMessage.length(), 500)); + } + + public void scheduleStatusMessageSend() { + if (!shouldSendEvent()) { + return; + } + scheduleInsightsTask = + scheduleInsightsTask( + insightsConfiguration.getStatusEventDelayMillis(), + insightsConfiguration.getExecutor(), + this::sendStatusMessage); + } + + public void shutdown() { + if (scheduleInsightsTask != null) { + scheduleInsightsTask.cancel(false); + } + } + + @VisibleForTesting + public CompletionStage sendStatusMessage() { + try { + String statusMessage = createStatusMessage(); + CompletionStage result = sendJsonMessage(statusMessage); + return result.whenComplete( + (aVoid, throwable) -> { + if (throwable != null) { + if (numberOfStatusEventErrors.getAndIncrement() < MAX_NUMBER_OF_STATUS_ERROR_LOGS) { + LOGGER.debug( + "Error while sending status message to Insights. Message was: " + + trimToFirst500characters(statusMessage), + throwable); + } + } + }); + } catch (Exception e) { + LOGGER.debug("Unexpected error while sending status message to Insights.", e); + return CompletableFutures.failedFuture(e); + } + } + + private CompletionStage sendJsonMessage(String jsonMessage) { + + QueryOptions queryOptions = createQueryOptionsWithJson(jsonMessage); + String logPrefix = driverContext.getSessionName(); + Duration timeout = + driverContext + .getConfig() + .getDefaultProfile() + .getDuration(DefaultDriverOption.CONTROL_CONNECTION_TIMEOUT); + LOGGER.debug("sending JSON message: {}", jsonMessage); + + Query query = new Query(REPORT_INSIGHT_RPC, queryOptions); + return AdminRequestHandler.call(controlConnection.channel(), query, timeout, logPrefix).start(); + } + + private QueryOptions createQueryOptionsWithJson(String json) { + TypeCodec codec = + driverContext.getCodecRegistry().codecFor(DataTypes.TEXT, String.class); + ByteBuffer startupMessageSerialized = codec.encode(json, DseProtocolVersion.DSE_V2); + return new QueryOptions( + QueryOptions.DEFAULT.consistency, + Collections.singletonList(startupMessageSerialized), + QueryOptions.DEFAULT.namedValues, + QueryOptions.DEFAULT.skipMetadata, + QueryOptions.DEFAULT.pageSize, + QueryOptions.DEFAULT.pagingState, + QueryOptions.DEFAULT.serialConsistency, + QueryOptions.DEFAULT.defaultTimestamp, + QueryOptions.DEFAULT.keyspace, + QueryOptions.DEFAULT.nowInSeconds); + } + + private boolean shouldSendEvent() { + try { + return insightsConfiguration.isMonitorReportingEnabled() + && InsightsSupportVerifier.supportsInsights( + driverContext.getMetadataManager().getMetadata().getNodes().values()); + } catch (Exception e) { + LOGGER.debug("Unexpected error while checking Insights support.", e); + return false; + } + } + + @VisibleForTesting + String createStartupMessage() { + InsightMetadata insightMetadata = createMetadata(STARTUP_MESSAGE_NAME, STARTUP_VERSION_1_ID); + InsightsStartupData data = createStartupData(); + + try { + return OBJECT_MAPPER.writeValueAsString(new Insight<>(insightMetadata, data)); + } catch (JsonProcessingException e) { + throw new InsightEventFormatException("Problem when creating: " + STARTUP_MESSAGE_NAME, e); + } + } + + @VisibleForTesting + String createStatusMessage() { + InsightMetadata insightMetadata = createMetadata(STATUS_MESSAGE_NAME, STATUS_VERSION_1_ID); + InsightsStatusData data = createStatusData(); + + try { + return OBJECT_MAPPER.writeValueAsString(new Insight<>(insightMetadata, data)); + } catch (JsonProcessingException e) { + throw new InsightEventFormatException("Problem when creating: " + STATUS_MESSAGE_NAME, e); + } + } + + private InsightsStatusData createStatusData() { + Map startupOptions = driverContext.getStartupOptions(); + return InsightsStatusData.builder() + .withClientId(getClientId(startupOptions)) + .withSessionId(id) + .withControlConnection(getControlConnectionSocketAddress()) + .withConnectedNodes(getConnectedNodes()) + .build(); + } + + private Map getConnectedNodes() { + Map pools = driverContext.getPoolManager().getPools(); + return pools.entrySet().stream() + .collect( + Collectors.toMap( + entry -> AddressFormatter.nullSafeToString(entry.getKey().getEndPoint().resolve()), + this::constructSessionStateForNode)); + } + + private SessionStateForNode constructSessionStateForNode(Map.Entry entry) { + return new SessionStateForNode( + entry.getKey().getOpenConnections(), entry.getValue().getInFlight()); + } + + private InsightsStartupData createStartupData() { + Map startupOptions = driverContext.getStartupOptions(); + return InsightsStartupData.builder() + .withClientId(getClientId(startupOptions)) + .withSessionId(id) + .withApplicationName(getApplicationName(startupOptions)) + .withApplicationVersion(getApplicationVersion(startupOptions)) + .withDriverName(getDriverName(startupOptions)) + .withDriverVersion(getDriverVersion(startupOptions)) + .withContactPoints( + getResolvedContactPoints( + driverContext.getMetadataManager().getContactPoints().stream() + .map(n -> n.getEndPoint().resolve()) + .filter(InetSocketAddress.class::isInstance) + .map(InetSocketAddress.class::cast) + .collect(Collectors.toSet()))) + .withInitialControlConnection(getControlConnectionSocketAddress()) + .withProtocolVersion(driverContext.getProtocolVersion().getCode()) + .withLocalAddress(getLocalAddress()) + .withExecutionProfiles(executionProfilesInfoFinder.getExecutionProfilesInfo(driverContext)) + .withPoolSizeByHostDistance(getPoolSizeByHostDistance()) + .withHeartbeatInterval( + driverContext + .getConfig() + .getDefaultProfile() + .getDuration(HEARTBEAT_INTERVAL) + .toMillis()) + .withCompression( + driverContext.getConfig().getDefaultProfile().getString(PROTOCOL_COMPRESSION, "none")) + .withReconnectionPolicy( + reconnectionPolicyInfoInfoFinder.getReconnectionPolicyInfo( + driverContext.getReconnectionPolicy(), + driverContext.getConfig().getDefaultProfile())) + .withSsl(getSsl()) + .withAuthProvider(getAuthProvider()) + .withOtherOptions(getOtherOptions()) + .withPlatformInfo(platformInfoFinder.getInsightsPlatformInfo()) + .withConfigAntiPatterns(configAntiPatternsFinder.findAntiPatterns(driverContext)) + .withPeriodicStatusInterval(getPeriodicStatusInterval()) + .withHostName(getLocalHostName()) + .withApplicationNameWasGenerated(isApplicationNameGenerated(startupOptions)) + .withDataCenters(dataCentersFinder.getDataCenters(driverContext)) + .build(); + } + + private AuthProviderType getAuthProvider() { + String authProviderClassName = + driverContext + .getConfig() + .getDefaultProfile() + .getString(AUTH_PROVIDER_CLASS, "NoAuthProvider"); + ClassSettingDetails authProviderDetails = + PackageUtil.getAuthProviderDetails(authProviderClassName); + return new AuthProviderType( + authProviderDetails.getClassName(), authProviderDetails.getFullPackage()); + } + + private long getPeriodicStatusInterval() { + return TimeUnit.MILLISECONDS.toSeconds(insightsConfiguration.getStatusEventDelayMillis()); + } + + @VisibleForTesting + static Map> getResolvedContactPoints(Set contactPoints) { + if (contactPoints == null) { + return Collections.emptyMap(); + } + return contactPoints.stream() + .collect( + Collectors.groupingBy( + InetSocketAddress::getHostName, + Collectors.mapping(AddressFormatter::nullSafeToString, Collectors.toList()))); + } + + private String getDriverVersion(Map startupOptions) { + return startupOptions.get(StartupOptionsBuilder.DRIVER_VERSION_KEY); + } + + private String getDriverName(Map startupOptions) { + return startupOptions.get(StartupOptionsBuilder.DRIVER_NAME_KEY); + } + + private String getClientId(Map startupOptions) { + return startupOptions.get(StartupOptionsBuilder.CLIENT_ID_KEY); + } + + private boolean isApplicationNameGenerated(Map startupOptions) { + return startupOptions.get(StartupOptionsBuilder.APPLICATION_NAME_KEY) == null; + } + + private String getApplicationVersion(Map startupOptions) { + String applicationVersion = startupOptions.get(StartupOptionsBuilder.APPLICATION_VERSION_KEY); + if (applicationVersion == null) { + return ""; + } + return applicationVersion; + } + + private String getApplicationName(Map startupOptions) { + String applicationName = startupOptions.get(StartupOptionsBuilder.APPLICATION_NAME_KEY); + if (applicationName == null || applicationName.isEmpty()) { + return getClusterCreateCaller(initCallStackTrace); + } + return applicationName; + } + + @VisibleForTesting + static String getClusterCreateCaller(StackTraceElement[] stackTrace) { + for (int i = 0; i < stackTrace.length - 1; i++) { + if (isClusterStackTrace(stackTrace[i])) { + int nextElement = i + 1; + if (!isClusterStackTrace(stackTrace[nextElement])) { + return stackTrace[nextElement].getClassName(); + } + } + } + return DEFAULT_JAVA_APPLICATION; + } + + private static boolean isClusterStackTrace(StackTraceElement stackTraceElement) { + return stackTraceElement.getClassName().equals(DefaultDriverContext.class.getName()) + || stackTraceElement.getClassName().equals(SessionBuilder.class.getName()); + } + + private String getLocalHostName() { + try { + return InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException e) { + LOGGER.warn("Can not resolve the name of a host, returning null", e); + return null; + } + } + + private Map getOtherOptions() { + return Collections.emptyMap(); // todo + } + + private SSL getSsl() { + boolean isSslDefined = + driverContext.getConfig().getDefaultProfile().isDefined(SSL_ENGINE_FACTORY_CLASS); + boolean certValidation = + driverContext.getConfig().getDefaultProfile().getBoolean(SSL_HOSTNAME_VALIDATION, false); + return new SSL(isSslDefined, certValidation); + } + + private PoolSizeByHostDistance getPoolSizeByHostDistance() { + + return new PoolSizeByHostDistance( + driverContext.getConfig().getDefaultProfile().getInt(CONNECTION_POOL_LOCAL_SIZE), + driverContext.getConfig().getDefaultProfile().getInt(CONNECTION_POOL_REMOTE_SIZE), + 0); + } + + private String getControlConnectionSocketAddress() { + SocketAddress controlConnectionAddress = controlConnection.channel().getEndPoint().resolve(); + return AddressFormatter.nullSafeToString(controlConnectionAddress); + } + + private String getLocalAddress() { + SocketAddress controlConnectionLocalAddress = controlConnection.channel().localAddress(); + if (controlConnectionLocalAddress instanceof InetSocketAddress) { + return AddressFormatter.nullSafeToString( + ((InetSocketAddress) controlConnectionLocalAddress).getAddress()); + } + return null; + } + + private InsightMetadata createMetadata(String messageName, String messageVersion) { + return new InsightMetadata( + messageName, timestampSupplier.get(), TAGS, InsightType.EVENT, messageVersion); + } + + @VisibleForTesting + static ScheduledFuture scheduleInsightsTask( + long statusEventDelayMillis, + ScheduledExecutorService scheduledTasksExecutor, + Runnable runnable) { + long initialDelay = + (long) Math.floor(statusEventDelayMillis - zeroToTenPercentRandom(statusEventDelayMillis)); + return scheduledTasksExecutor.scheduleWithFixedDelay( + runnable, initialDelay, statusEventDelayMillis, TimeUnit.MILLISECONDS); + } + + private static double zeroToTenPercentRandom(long statusEventDelayMillis) { + return 0.1 * statusEventDelayMillis * Math.random(); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/insights/InsightsSupportVerifier.java b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/InsightsSupportVerifier.java new file mode 100644 index 00000000000..ec016ef52d8 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/InsightsSupportVerifier.java @@ -0,0 +1,50 @@ +/* + * 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 com.datastax.dse.driver.internal.core.insights; + +import com.datastax.dse.driver.api.core.metadata.DseNodeProperties; +import com.datastax.oss.driver.api.core.Version; +import com.datastax.oss.driver.api.core.metadata.Node; +import java.util.Collection; + +class InsightsSupportVerifier { + private static final Version minDse6Version = Version.parse("6.0.5"); + private static final Version minDse51Version = Version.parse("5.1.13"); + private static final Version dse600Version = Version.parse("6.0.0"); + + static boolean supportsInsights(Collection nodes) { + assert minDse6Version != null; + assert dse600Version != null; + assert minDse51Version != null; + if (nodes.isEmpty()) return false; + + for (Node node : nodes) { + Object version = node.getExtras().get(DseNodeProperties.DSE_VERSION); + if (version == null) { + return false; + } + Version dseVersion = (Version) version; + if (!(dseVersion.compareTo(minDse6Version) >= 0 + || (dseVersion.compareTo(dse600Version) < 0 + && dseVersion.compareTo(minDse51Version) >= 0))) { + return false; + } + } + return true; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/insights/PackageUtil.java b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/PackageUtil.java new file mode 100644 index 00000000000..3c61dec4f20 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/PackageUtil.java @@ -0,0 +1,95 @@ +/* + * 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 com.datastax.dse.driver.internal.core.insights; + +import com.datastax.oss.driver.shaded.guava.common.annotations.VisibleForTesting; +import com.datastax.oss.driver.shaded.guava.common.base.Joiner; +import java.util.Arrays; +import java.util.regex.Pattern; + +class PackageUtil { + static final String DEFAULT_SPECULATIVE_EXECUTION_PACKAGE = + "com.datastax.oss.driver.internal.core.specex"; + static final String DEFAULT_LOAD_BALANCING_PACKAGE = + "com.datastax.oss.driver.internal.core.loadbalancing"; + static final String DEFAULT_AUTH_PROVIDER_PACKAGE = "com.datastax.oss.driver.internal.core.auth"; + private static final Pattern PACKAGE_SPLIT_REGEX = Pattern.compile("\\."); + private static final Joiner DOT_JOINER = Joiner.on("."); + + static String getNamespace(Class tClass) { + String namespace = ""; + Package packageInfo = tClass.getPackage(); + if (packageInfo != null) { + namespace = packageInfo.getName(); + } + return namespace; + } + + static ClassSettingDetails getSpeculativeExecutionDetails(String classSetting) { + return getClassSettingDetails(classSetting, DEFAULT_SPECULATIVE_EXECUTION_PACKAGE); + } + + static ClassSettingDetails getLoadBalancingDetails(String classSetting) { + return getClassSettingDetails(classSetting, DEFAULT_LOAD_BALANCING_PACKAGE); + } + + static ClassSettingDetails getAuthProviderDetails(String classSetting) { + return getClassSettingDetails(classSetting, DEFAULT_AUTH_PROVIDER_PACKAGE); + } + + private static ClassSettingDetails getClassSettingDetails( + String classSetting, String packageName) { + String className = getClassName(classSetting); + String fullPackage = getFullPackageOrDefault(classSetting, packageName); + return new ClassSettingDetails(className, fullPackage); + } + + @VisibleForTesting + static String getClassName(String classSetting) { + String[] split = PACKAGE_SPLIT_REGEX.split(classSetting); + if (split.length == 0) { + return ""; + } + return split[split.length - 1]; + } + + @VisibleForTesting + static String getFullPackageOrDefault(String classSetting, String defaultValue) { + String[] split = PACKAGE_SPLIT_REGEX.split(classSetting); + if (split.length <= 1) return defaultValue; + return DOT_JOINER.join(Arrays.copyOf(split, split.length - 1)); + } + + static class ClassSettingDetails { + private final String className; + private final String fullPackage; + + ClassSettingDetails(String className, String fullPackage) { + this.className = className; + this.fullPackage = fullPackage; + } + + String getClassName() { + return className; + } + + String getFullPackage() { + return fullPackage; + } + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/insights/PlatformInfoFinder.java b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/PlatformInfoFinder.java new file mode 100644 index 00000000000..30d41d40836 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/PlatformInfoFinder.java @@ -0,0 +1,295 @@ +/* + * 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 com.datastax.dse.driver.internal.core.insights; + +import static com.datastax.dse.driver.internal.core.insights.schema.InsightsPlatformInfo.OS; +import static com.datastax.dse.driver.internal.core.insights.schema.InsightsPlatformInfo.RuntimeAndCompileTimeVersions; + +import com.datastax.dse.driver.internal.core.insights.schema.InsightsPlatformInfo; +import com.datastax.dse.driver.internal.core.insights.schema.InsightsPlatformInfo.CPUS; +import com.datastax.oss.driver.internal.core.os.Native; +import com.datastax.oss.driver.shaded.guava.common.annotations.VisibleForTesting; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Properties; +import java.util.function.Function; +import java.util.regex.Pattern; + +class PlatformInfoFinder { + private static final String MAVEN_IGNORE_LINE = "The following files have been resolved:"; + private static final Pattern DEPENDENCY_SPLIT_REGEX = Pattern.compile(":"); + static final String UNVERIFIED_RUNTIME_VERSION = "UNVERIFIED"; + private final Function propertiesUrlProvider; + + @SuppressWarnings("UnnecessaryLambda") + private static final Function M2_PROPERTIES_PROVIDER = + d -> { + ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + if (contextClassLoader == null) { + contextClassLoader = PlatformInfoFinder.class.getClassLoader(); + } + return contextClassLoader.getResource( + "META-INF/maven/" + d.groupId + "/" + d.artifactId + "/pom.properties"); + }; + + PlatformInfoFinder() { + this(M2_PROPERTIES_PROVIDER); + } + + @VisibleForTesting + PlatformInfoFinder(Function pomPropertiesUrlProvider) { + this.propertiesUrlProvider = pomPropertiesUrlProvider; + } + + InsightsPlatformInfo getInsightsPlatformInfo() { + OS os = getOsInfo(); + CPUS cpus = getCpuInfo(); + Map> runtimeInfo = getRuntimeInfo(); + + return new InsightsPlatformInfo(os, cpus, runtimeInfo); + } + + private Map> getRuntimeInfo() { + Map coreDeps = + fetchDependenciesFromFile( + this.getClass().getResourceAsStream("/com/datastax/dse/driver/internal/deps.txt")); + + Map queryBuilderDeps = + fetchDependenciesFromFile( + this.getClass() + .getResourceAsStream("/com/datastax/dse/driver/internal/querybuilder/deps.txt")); + + Map mapperProcessorDeps = + fetchDependenciesFromFile( + this.getClass() + .getResourceAsStream( + "/com/datastax/dse/driver/internal/mapper/processor/deps.txt")); + + Map mapperRuntimeDeps = + fetchDependenciesFromFile( + this.getClass() + .getResourceAsStream("/com/datastax/dse/driver/internal/mapper/deps.txt")); + + Map> runtimeDependencies = + new LinkedHashMap<>(); + putIfNonEmpty(coreDeps, runtimeDependencies, "core"); + putIfNonEmpty(queryBuilderDeps, runtimeDependencies, "query-builder"); + putIfNonEmpty(mapperProcessorDeps, runtimeDependencies, "mapper-processor"); + putIfNonEmpty(mapperRuntimeDeps, runtimeDependencies, "mapper-runtime"); + addJavaVersion(runtimeDependencies); + return runtimeDependencies; + } + + private void putIfNonEmpty( + Map moduleDependencies, + Map> runtimeDependencies, + String moduleName) { + if (!moduleDependencies.isEmpty()) { + runtimeDependencies.put(moduleName, moduleDependencies); + } + } + + @VisibleForTesting + void addJavaVersion(Map> runtimeDependencies) { + Package javaPackage = Runtime.class.getPackage(); + Map javaDependencies = new LinkedHashMap<>(); + javaDependencies.put( + "version", toSameRuntimeAndCompileVersion(javaPackage.getImplementationVersion())); + javaDependencies.put( + "vendor", toSameRuntimeAndCompileVersion(javaPackage.getImplementationVendor())); + javaDependencies.put( + "title", toSameRuntimeAndCompileVersion(javaPackage.getImplementationTitle())); + putIfNonEmpty(javaDependencies, runtimeDependencies, "java"); + } + + private RuntimeAndCompileTimeVersions toSameRuntimeAndCompileVersion(String version) { + return new RuntimeAndCompileTimeVersions(version, version, false); + } + + /** + * Method is fetching dependencies from file. Lines in file should be in format: + * com.organization:artifactId:jar:1.2.0 or com.organization:artifactId:jar:native:1.2.0 + * + *

For such file the output will be: Map + * "com.organization:artifactId",{"runtimeVersion":"1.2.0", "compileVersion:"1.2.0", "optional": + * false} Duplicates will be omitted. If there are two dependencies for the exactly the same + * organizationId:artifactId it is not deterministic which version will be taken. In the case of + * an error while opening file this method will fail silently returning an empty Map + */ + @VisibleForTesting + Map fetchDependenciesFromFile(InputStream inputStream) { + Map dependencies = new LinkedHashMap<>(); + if (inputStream == null) { + return dependencies; + } + try { + List dependenciesFromFile = extractMavenDependenciesFromFile(inputStream); + for (DependencyFromFile d : dependenciesFromFile) { + dependencies.put(formatDependencyName(d), getRuntimeAndCompileVersion(d)); + } + } catch (IOException e) { + return dependencies; + } + return dependencies; + } + + private RuntimeAndCompileTimeVersions getRuntimeAndCompileVersion(DependencyFromFile d) { + URL url = propertiesUrlProvider.apply(d); + if (url == null) { + return new RuntimeAndCompileTimeVersions( + UNVERIFIED_RUNTIME_VERSION, d.getVersion(), d.isOptional()); + } + Properties properties = new Properties(); + try { + properties.load(url.openStream()); + } catch (IOException e) { + return new RuntimeAndCompileTimeVersions( + UNVERIFIED_RUNTIME_VERSION, d.getVersion(), d.isOptional()); + } + Object version = properties.get("version"); + if (version == null) { + return new RuntimeAndCompileTimeVersions( + UNVERIFIED_RUNTIME_VERSION, d.getVersion(), d.isOptional()); + } else { + return new RuntimeAndCompileTimeVersions(version.toString(), d.getVersion(), d.isOptional()); + } + } + + private String formatDependencyName(DependencyFromFile d) { + return String.format("%s:%s", d.getGroupId(), d.getArtifactId()); + } + + private List extractMavenDependenciesFromFile(InputStream inputStream) + throws IOException { + List dependenciesFromFile = new ArrayList<>(); + BufferedReader reader = + new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + for (String line; (line = reader.readLine()) != null; ) { + if (lineWithDependencyInfo(line)) { + dependenciesFromFile.add(extractDependencyFromLine(line.trim())); + } + } + return dependenciesFromFile; + } + + private DependencyFromFile extractDependencyFromLine(String line) { + String[] split = DEPENDENCY_SPLIT_REGEX.split(line); + if (split.length == 6) { // case for i.e.: com.github.jnr:jffi:jar:native:1.2.16:compile + return new DependencyFromFile(split[0], split[1], split[4], checkIsOptional(split[5])); + } else { // case for normal: org.ow2.asm:asm:jar:5.0.3:compile + return new DependencyFromFile(split[0], split[1], split[3], checkIsOptional(split[4])); + } + } + + private boolean checkIsOptional(String scope) { + return scope.contains("(optional)"); + } + + private boolean lineWithDependencyInfo(String line) { + return (!line.equals(MAVEN_IGNORE_LINE) && !line.isEmpty()); + } + + private CPUS getCpuInfo() { + int numberOfProcessors = Runtime.getRuntime().availableProcessors(); + String model = Native.getCpu(); + return new CPUS(numberOfProcessors, model); + } + + private OS getOsInfo() { + String osName = System.getProperty("os.name"); + String osVersion = System.getProperty("os.version"); + String osArch = System.getProperty("os.arch"); + return new OS(osName, osVersion, osArch); + } + + static class DependencyFromFile { + private final String groupId; + private final String artifactId; + private final String version; + private final boolean optional; + + DependencyFromFile(String groupId, String artifactId, String version, boolean optional) { + this.groupId = groupId; + this.artifactId = artifactId; + this.version = version; + this.optional = optional; + } + + String getGroupId() { + return groupId; + } + + String getArtifactId() { + return artifactId; + } + + String getVersion() { + return version; + } + + boolean isOptional() { + return optional; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof DependencyFromFile)) { + return false; + } + DependencyFromFile that = (DependencyFromFile) o; + return optional == that.optional + && Objects.equals(groupId, that.groupId) + && Objects.equals(artifactId, that.artifactId) + && Objects.equals(version, that.version); + } + + @Override + public int hashCode() { + return Objects.hash(groupId, artifactId, version, optional); + } + + @Override + public String toString() { + return "DependencyFromFile{" + + "groupId='" + + groupId + + '\'' + + ", artifactId='" + + artifactId + + '\'' + + ", version='" + + version + + '\'' + + ", optional=" + + optional + + '}'; + } + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/insights/ReconnectionPolicyInfoFinder.java b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/ReconnectionPolicyInfoFinder.java new file mode 100644 index 00000000000..af8aff74035 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/ReconnectionPolicyInfoFinder.java @@ -0,0 +1,49 @@ +/* + * 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 com.datastax.dse.driver.internal.core.insights; + +import com.datastax.dse.driver.internal.core.insights.schema.ReconnectionPolicyInfo; +import com.datastax.oss.driver.api.core.config.DefaultDriverOption; +import com.datastax.oss.driver.api.core.config.DriverExecutionProfile; +import com.datastax.oss.driver.api.core.connection.ReconnectionPolicy; +import com.datastax.oss.driver.internal.core.connection.ConstantReconnectionPolicy; +import com.datastax.oss.driver.internal.core.connection.ExponentialReconnectionPolicy; +import java.util.HashMap; +import java.util.Map; + +class ReconnectionPolicyInfoFinder { + ReconnectionPolicyInfo getReconnectionPolicyInfo( + ReconnectionPolicy reconnectionPolicy, DriverExecutionProfile executionProfile) { + Class reconnectionPolicyClass = reconnectionPolicy.getClass(); + String type = reconnectionPolicyClass.getSimpleName(); + String namespace = PackageUtil.getNamespace(reconnectionPolicyClass); + Map options = new HashMap<>(); + if (reconnectionPolicy instanceof ConstantReconnectionPolicy) { + options.put( + "delayMs", + executionProfile.getDuration(DefaultDriverOption.RECONNECTION_BASE_DELAY).toMillis()); + } else if (reconnectionPolicy instanceof ExponentialReconnectionPolicy) { + ExponentialReconnectionPolicy exponentialReconnectionPolicy = + (ExponentialReconnectionPolicy) reconnectionPolicy; + options.put("maxDelayMs", exponentialReconnectionPolicy.getMaxDelayMs()); + options.put("baseDelayMs", exponentialReconnectionPolicy.getBaseDelayMs()); + options.put("maxAttempts", exponentialReconnectionPolicy.getMaxAttempts()); + } + return new ReconnectionPolicyInfo(type, options, namespace); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/insights/configuration/InsightsConfiguration.java b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/configuration/InsightsConfiguration.java new file mode 100644 index 00000000000..ac27bb76389 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/configuration/InsightsConfiguration.java @@ -0,0 +1,45 @@ +/* + * 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 com.datastax.dse.driver.internal.core.insights.configuration; + +import io.netty.util.concurrent.EventExecutor; + +public class InsightsConfiguration { + private final boolean monitorReportingEnabled; + private final long statusEventDelayMillis; + private final EventExecutor executor; + + public InsightsConfiguration( + boolean monitorReportingEnabled, long statusEventDelayMillis, EventExecutor executor) { + this.monitorReportingEnabled = monitorReportingEnabled; + this.statusEventDelayMillis = statusEventDelayMillis; + this.executor = executor; + } + + public boolean isMonitorReportingEnabled() { + return monitorReportingEnabled; + } + + public long getStatusEventDelayMillis() { + return statusEventDelayMillis; + } + + public EventExecutor getExecutor() { + return executor; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/insights/exceptions/InsightEventFormatException.java b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/exceptions/InsightEventFormatException.java new file mode 100644 index 00000000000..cfce68971ef --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/exceptions/InsightEventFormatException.java @@ -0,0 +1,25 @@ +/* + * 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 com.datastax.dse.driver.internal.core.insights.exceptions; + +public class InsightEventFormatException extends RuntimeException { + + public InsightEventFormatException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/AuthProviderType.java b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/AuthProviderType.java new file mode 100644 index 00000000000..18aec53e899 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/AuthProviderType.java @@ -0,0 +1,67 @@ +/* + * 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 com.datastax.dse.driver.internal.core.insights.schema; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; + +public class AuthProviderType { + @JsonProperty("type") + private final String type; + + @JsonProperty("namespace") + private final String namespace; + + @JsonCreator + public AuthProviderType( + @JsonProperty("type") String type, @JsonProperty("namespace") String namespace) { + this.type = type; + this.namespace = namespace; + } + + public String getType() { + return type; + } + + public String getNamespace() { + return namespace; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof AuthProviderType)) { + return false; + } + AuthProviderType that = (AuthProviderType) o; + return Objects.equals(type, that.type) && Objects.equals(namespace, that.namespace); + } + + @Override + public int hashCode() { + return Objects.hash(type, namespace); + } + + @Override + public String toString() { + return "AuthProviderType{" + "type='" + type + '\'' + ", namespace='" + namespace + '\'' + '}'; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/Insight.java b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/Insight.java new file mode 100644 index 00000000000..ca4e6455345 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/Insight.java @@ -0,0 +1,52 @@ +/* + * 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 com.datastax.dse.driver.internal.core.insights.schema; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public class Insight { + @JsonProperty("metadata") + private final InsightMetadata metadata; + + @JsonProperty("data") + private final T insightData; + + @JsonCreator + public Insight(@JsonProperty("metadata") InsightMetadata metadata, @JsonProperty("data") T data) { + this.metadata = metadata; + this.insightData = data; + } + + public InsightMetadata getMetadata() { + return metadata; + } + + public T getInsightData() { + return insightData; + } + + @Override + public String toString() { + return "Insight{" + "metadata=" + metadata + ", insightData=" + insightData + '}'; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/InsightMetadata.java b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/InsightMetadata.java new file mode 100644 index 00000000000..cfa2644b0c7 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/InsightMetadata.java @@ -0,0 +1,118 @@ +/* + * 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 com.datastax.dse.driver.internal.core.insights.schema; + +import com.datastax.oss.driver.shaded.guava.common.base.Preconditions; +import com.datastax.oss.driver.shaded.guava.common.base.Strings; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Map; +import java.util.Objects; + +public class InsightMetadata { + @JsonProperty("name") + private final String name; + + @JsonProperty("timestamp") + private final long timestamp; + + @JsonProperty("tags") + private final Map tags; + + @JsonProperty("insightType") + private final InsightType insightType; + + @JsonProperty("insightMappingId") + @JsonInclude(JsonInclude.Include.NON_NULL) + private String insightMappingId; + + @JsonCreator + public InsightMetadata( + @JsonProperty("name") String name, + @JsonProperty("timestamp") long timestamp, + @JsonProperty("tags") Map tags, + @JsonProperty("insightType") InsightType insightType, + @JsonProperty("insightMappingId") String insightMappingId) { + Preconditions.checkArgument(!Strings.isNullOrEmpty(name), "name is required"); + + this.name = name; + this.timestamp = timestamp; + this.tags = tags; + this.insightType = insightType; + this.insightMappingId = insightMappingId; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof InsightMetadata)) { + return false; + } + InsightMetadata that = (InsightMetadata) o; + return Objects.equals(name, that.name) + && Objects.equals(timestamp, that.timestamp) + && Objects.equals(tags, that.tags) + && insightType == that.insightType + && Objects.equals(insightMappingId, that.insightMappingId); + } + + @Override + public int hashCode() { + return Objects.hash(name, timestamp, tags, insightType, insightMappingId); + } + + @Override + public String toString() { + return "InsightMetadata{" + + "name='" + + name + + '\'' + + ", timestamp=" + + timestamp + + ", tags=" + + tags + + ", insightType=" + + insightType + + ", insightMappingId=" + + insightMappingId + + '}'; + } + + public String getName() { + return name; + } + + public long getTimestamp() { + return timestamp; + } + + public Map getTags() { + return tags; + } + + public InsightType getInsightType() { + return insightType; + } + + public String getInsightMappingId() { + return insightMappingId; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/InsightType.java b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/InsightType.java new file mode 100644 index 00000000000..ae91e27d227 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/InsightType.java @@ -0,0 +1,28 @@ +/* + * 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 com.datastax.dse.driver.internal.core.insights.schema; + +public enum InsightType { + EVENT, + GAUGE, + COUNTER, + HISTOGRAM, + TIMER, + METER, + LOG; +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/InsightsPlatformInfo.java b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/InsightsPlatformInfo.java new file mode 100644 index 00000000000..231f082d785 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/InsightsPlatformInfo.java @@ -0,0 +1,236 @@ +/* + * 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 com.datastax.dse.driver.internal.core.insights.schema; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Map; +import java.util.Objects; + +public class InsightsPlatformInfo { + @JsonProperty("os") + private final OS os; + + @JsonProperty("cpus") + private CPUS cpus; + + /** + * All dependencies in a map format grouped by the module: {"core" : {"com.datastax.driver:core": + * {"runtimeVersion:" : "1.0.0", "compileVersion": "1.0.1"},...}}, "extras"" {...} + */ + @JsonProperty("runtime") + private Map> runtime; + + @JsonCreator + public InsightsPlatformInfo( + @JsonProperty("os") OS os, + @JsonProperty("cpus") CPUS cpus, + @JsonProperty("runtime") Map> runtime) { + this.os = os; + this.cpus = cpus; + this.runtime = runtime; + } + + public OS getOs() { + return os; + } + + public CPUS getCpus() { + return cpus; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof InsightsPlatformInfo)) { + return false; + } + InsightsPlatformInfo that = (InsightsPlatformInfo) o; + return Objects.equals(os, that.os) + && Objects.equals(cpus, that.cpus) + && Objects.equals(runtime, that.runtime); + } + + @Override + public int hashCode() { + return Objects.hash(os, cpus, runtime); + } + + Map> getRuntime() { + return runtime; + } + + public static class OS { + @JsonProperty("name") + private final String name; + + @JsonProperty("version") + private final String version; + + @JsonProperty("arch") + private final String arch; + + @JsonCreator + public OS( + @JsonProperty("name") String name, + @JsonProperty("version") String version, + @JsonProperty("arch") String arch) { + this.name = name; + this.version = version; + this.arch = arch; + } + + public String getName() { + return name; + } + + public String getVersion() { + return version; + } + + public String getArch() { + return arch; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof OS)) { + return false; + } + OS os = (OS) o; + return Objects.equals(name, os.name) + && Objects.equals(version, os.version) + && Objects.equals(arch, os.arch); + } + + @Override + public int hashCode() { + return Objects.hash(name, version, arch); + } + } + + public static class CPUS { + @JsonProperty("length") + private final int length; + + @JsonProperty("model") + private final String model; + + @JsonCreator + public CPUS(@JsonProperty("length") int length, @JsonProperty("model") String model) { + this.length = length; + this.model = model; + } + + public int getLength() { + return length; + } + + public String getModel() { + return model; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof CPUS)) { + return false; + } + CPUS cpus = (CPUS) o; + return length == cpus.length && Objects.equals(model, cpus.model); + } + + @Override + public int hashCode() { + return Objects.hash(length, model); + } + } + + public static class RuntimeAndCompileTimeVersions { + @JsonProperty("runtimeVersion") + private final String runtimeVersion; + + @JsonProperty("compileVersion") + private final String compileVersion; + + @JsonProperty("optional") + private final boolean optional; + + @JsonCreator + public RuntimeAndCompileTimeVersions( + @JsonProperty("runtimeVersion") String runtimeVersion, + @JsonProperty("compileVersion") String compileVersion, + @JsonProperty("optional") boolean optional) { + this.runtimeVersion = runtimeVersion; + this.compileVersion = compileVersion; + this.optional = optional; + } + + public String getRuntimeVersion() { + return runtimeVersion; + } + + public String getCompileVersion() { + return compileVersion; + } + + public boolean isOptional() { + return optional; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof RuntimeAndCompileTimeVersions)) { + return false; + } + RuntimeAndCompileTimeVersions that = (RuntimeAndCompileTimeVersions) o; + return optional == that.optional + && Objects.equals(runtimeVersion, that.runtimeVersion) + && Objects.equals(compileVersion, that.compileVersion); + } + + @Override + public int hashCode() { + return Objects.hash(runtimeVersion, compileVersion, optional); + } + + @Override + public String toString() { + return "RuntimeAndCompileTimeVersions{" + + "runtimeVersion='" + + runtimeVersion + + '\'' + + ", compileVersion='" + + compileVersion + + '\'' + + ", optional=" + + optional + + '}'; + } + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/InsightsStartupData.java b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/InsightsStartupData.java new file mode 100644 index 00000000000..bddd3ef94b3 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/InsightsStartupData.java @@ -0,0 +1,425 @@ +/* + * 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 com.datastax.dse.driver.internal.core.insights.schema; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class InsightsStartupData { + @JsonProperty("clientId") + private final String clientId; + + @JsonProperty("sessionId") + private final String sessionId; + + @JsonProperty("applicationName") + private final String applicationName; + + @JsonProperty("applicationVersion") + private final String applicationVersion; + + @JsonProperty("contactPoints") + private final Map> contactPoints; + + @JsonProperty("initialControlConnection") + private final String initialControlConnection; + + @JsonProperty("protocolVersion") + private final int protocolVersion; + + @JsonProperty("localAddress") + private final String localAddress; + + @JsonProperty("executionProfiles") + private final Map executionProfiles; + + @JsonProperty("poolSizeByHostDistance") + private final PoolSizeByHostDistance poolSizeByHostDistance; + + @JsonProperty("heartbeatInterval") + private final long heartbeatInterval; + + @JsonProperty("compression") + private final String compression; + + @JsonProperty("reconnectionPolicy") + private final ReconnectionPolicyInfo reconnectionPolicy; + + @JsonProperty("ssl") + private final SSL ssl; + + @JsonProperty("authProvider") + private final AuthProviderType authProvider; + + @JsonProperty("otherOptions") + private final Map otherOptions; + + @JsonProperty("configAntiPatterns") + private final Map configAntiPatterns; + + @JsonProperty("periodicStatusInterval") + private final long periodicStatusInterval; + + @JsonProperty("platformInfo") + private final InsightsPlatformInfo platformInfo; + + @JsonProperty("hostName") + private final String hostName; + + @JsonProperty("driverName") + private String driverName; + + @JsonProperty("applicationNameWasGenerated") + private boolean applicationNameWasGenerated; + + @JsonProperty("driverVersion") + private String driverVersion; + + @JsonProperty("dataCenters") + private Set dataCenters; + + @JsonCreator + private InsightsStartupData( + @JsonProperty("clientId") String clientId, + @JsonProperty("sessionId") String sessionId, + @JsonProperty("applicationName") String applicationName, + @JsonProperty("applicationVersion") String applicationVersion, + @JsonProperty("contactPoints") Map> contactPoints, + @JsonProperty("initialControlConnection") String initialControlConnection, + @JsonProperty("protocolVersion") int protocolVersion, + @JsonProperty("localAddress") String localAddress, + @JsonProperty("executionProfiles") Map executionProfiles, + @JsonProperty("poolSizeByHostDistance") PoolSizeByHostDistance poolSizeByHostDistance, + @JsonProperty("heartbeatInterval") long heartbeatInterval, + @JsonProperty("compression") String compression, + @JsonProperty("reconnectionPolicy") ReconnectionPolicyInfo reconnectionPolicy, + @JsonProperty("ssl") SSL ssl, + @JsonProperty("authProvider") AuthProviderType authProvider, + @JsonProperty("otherOptions") Map otherOptions, + @JsonProperty("configAntiPatterns") Map configAntiPatterns, + @JsonProperty("periodicStatusInterval") long periodicStatusInterval, + @JsonProperty("platformInfo") InsightsPlatformInfo platformInfo, + @JsonProperty("hostName") String hostName, + @JsonProperty("driverName") String driverName, + @JsonProperty("applicationNameWasGenerated") boolean applicationNameWasGenerated, + @JsonProperty("driverVersion") String driverVersion, + @JsonProperty("dataCenters") Set dataCenters) { + this.clientId = clientId; + this.sessionId = sessionId; + this.applicationName = applicationName; + this.applicationVersion = applicationVersion; + this.contactPoints = contactPoints; + this.initialControlConnection = initialControlConnection; + this.protocolVersion = protocolVersion; + this.localAddress = localAddress; + this.executionProfiles = executionProfiles; + this.poolSizeByHostDistance = poolSizeByHostDistance; + this.heartbeatInterval = heartbeatInterval; + this.compression = compression; + this.reconnectionPolicy = reconnectionPolicy; + this.ssl = ssl; + this.authProvider = authProvider; + this.otherOptions = otherOptions; + this.configAntiPatterns = configAntiPatterns; + this.periodicStatusInterval = periodicStatusInterval; + this.platformInfo = platformInfo; + this.hostName = hostName; + this.driverName = driverName; + this.applicationNameWasGenerated = applicationNameWasGenerated; + this.driverVersion = driverVersion; + this.dataCenters = dataCenters; + } + + public String getClientId() { + return clientId; + } + + public String getSessionId() { + return sessionId; + } + + public String getApplicationName() { + return applicationName; + } + + public String getApplicationVersion() { + return applicationVersion; + } + + public Map> getContactPoints() { + return contactPoints; + } + + public String getInitialControlConnection() { + return initialControlConnection; + } + + public int getProtocolVersion() { + return protocolVersion; + } + + public String getLocalAddress() { + return localAddress; + } + + public Map getExecutionProfiles() { + return executionProfiles; + } + + public PoolSizeByHostDistance getPoolSizeByHostDistance() { + return poolSizeByHostDistance; + } + + public long getHeartbeatInterval() { + return heartbeatInterval; + } + + public String getCompression() { + return compression; + } + + public ReconnectionPolicyInfo getReconnectionPolicy() { + return reconnectionPolicy; + } + + public SSL getSsl() { + return ssl; + } + + public AuthProviderType getAuthProvider() { + return authProvider; + } + + public Map getOtherOptions() { + return otherOptions; + } + + public Map getConfigAntiPatterns() { + return configAntiPatterns; + } + + public long getPeriodicStatusInterval() { + return periodicStatusInterval; + } + + public InsightsPlatformInfo getPlatformInfo() { + return platformInfo; + } + + public String getHostName() { + return hostName; + } + + public String getDriverName() { + return driverName; + } + + public boolean isApplicationNameWasGenerated() { + return applicationNameWasGenerated; + } + + public String getDriverVersion() { + return driverVersion; + } + + public Set getDataCenters() { + return dataCenters; + } + + public static InsightsStartupData.Builder builder() { + return new InsightsStartupData.Builder(); + } + + public static class Builder { + private String clientId; + private String sessionId; + private String applicationName; + private String applicationVersion; + private Map> contactPoints; + private String initialControlConnection; + private int protocolVersion; + private String localAddress; + private Map executionProfiles; + private PoolSizeByHostDistance poolSizeByHostDistance; + private long heartbeatInterval; + private String compression; + private ReconnectionPolicyInfo reconnectionPolicy; + private SSL ssl; + private AuthProviderType authProvider; + private Map otherOptions; + private Map configAntiPatterns; + private long periodicStatusInterval; + private InsightsPlatformInfo platformInfo; + private String hostName; + private String driverName; + private String driverVersion; + private boolean applicationNameWasGenerated; + private Set dataCenters; + + public InsightsStartupData build() { + return new InsightsStartupData( + clientId, + sessionId, + applicationName, + applicationVersion, + contactPoints, + initialControlConnection, + protocolVersion, + localAddress, + executionProfiles, + poolSizeByHostDistance, + heartbeatInterval, + compression, + reconnectionPolicy, + ssl, + authProvider, + otherOptions, + configAntiPatterns, + periodicStatusInterval, + platformInfo, + hostName, + driverName, + applicationNameWasGenerated, + driverVersion, + dataCenters); + } + + public Builder withClientId(String clientId) { + this.clientId = clientId; + return this; + } + + public Builder withSessionId(String id) { + this.sessionId = id; + return this; + } + + public Builder withApplicationName(String applicationName) { + this.applicationName = applicationName; + return this; + } + + public Builder withApplicationVersion(String applicationVersion) { + this.applicationVersion = applicationVersion; + return this; + } + + public Builder withContactPoints(Map> contactPoints) { + this.contactPoints = contactPoints; + return this; + } + + public Builder withInitialControlConnection(String inetSocketAddress) { + this.initialControlConnection = inetSocketAddress; + return this; + } + + public Builder withProtocolVersion(int protocolVersion) { + this.protocolVersion = protocolVersion; + return this; + } + + public Builder withLocalAddress(String localAddress) { + this.localAddress = localAddress; + return this; + } + + public Builder withExecutionProfiles(Map executionProfiles) { + this.executionProfiles = executionProfiles; + return this; + } + + public Builder withPoolSizeByHostDistance(PoolSizeByHostDistance poolSizeByHostDistance) { + this.poolSizeByHostDistance = poolSizeByHostDistance; + return this; + } + + public Builder withHeartbeatInterval(long heartbeatInterval) { + this.heartbeatInterval = heartbeatInterval; + return this; + } + + public Builder withCompression(String compression) { + this.compression = compression; + return this; + } + + public Builder withReconnectionPolicy(ReconnectionPolicyInfo reconnectionPolicy) { + this.reconnectionPolicy = reconnectionPolicy; + return this; + } + + public Builder withSsl(SSL ssl) { + this.ssl = ssl; + return this; + } + + public Builder withAuthProvider(AuthProviderType authProvider) { + this.authProvider = authProvider; + return this; + } + + public Builder withOtherOptions(Map otherOptions) { + this.otherOptions = otherOptions; + return this; + } + + public Builder withConfigAntiPatterns(Map configAntiPatterns) { + this.configAntiPatterns = configAntiPatterns; + return this; + } + + public Builder withPeriodicStatusInterval(long periodicStatusInterval) { + this.periodicStatusInterval = periodicStatusInterval; + return this; + } + + public Builder withPlatformInfo(InsightsPlatformInfo insightsPlatformInfo) { + this.platformInfo = insightsPlatformInfo; + return this; + } + + public Builder withHostName(String hostName) { + this.hostName = hostName; + return this; + } + + public Builder withDriverName(String driverName) { + this.driverName = driverName; + return this; + } + + public Builder withDriverVersion(String driverVersion) { + this.driverVersion = driverVersion; + return this; + } + + public Builder withApplicationNameWasGenerated(boolean applicationNameWasGenerated) { + this.applicationNameWasGenerated = applicationNameWasGenerated; + return this; + } + + public Builder withDataCenters(Set dataCenters) { + this.dataCenters = dataCenters; + return this; + } + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/InsightsStatusData.java b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/InsightsStatusData.java new file mode 100644 index 00000000000..6f5a135f7c4 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/InsightsStatusData.java @@ -0,0 +1,136 @@ +/* + * 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 com.datastax.dse.driver.internal.core.insights.schema; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Map; +import java.util.Objects; + +public class InsightsStatusData { + @JsonProperty("clientId") + private final String clientId; + + @JsonProperty("sessionId") + private final String sessionId; + + @JsonProperty("controlConnection") + private final String controlConnection; + + @JsonProperty("connectedNodes") + private final Map connectedNodes; + + @JsonCreator + private InsightsStatusData( + @JsonProperty("clientId") String clientId, + @JsonProperty("sessionId") String sessionId, + @JsonProperty("controlConnection") String controlConnection, + @JsonProperty("connectedNodes") Map connectedNodes) { + this.clientId = clientId; + this.sessionId = sessionId; + this.controlConnection = controlConnection; + this.connectedNodes = connectedNodes; + } + + public String getClientId() { + return clientId; + } + + public String getSessionId() { + return sessionId; + } + + public String getControlConnection() { + return controlConnection; + } + + public Map getConnectedNodes() { + return connectedNodes; + } + + @Override + public String toString() { + return "InsightsStatusData{" + + "clientId='" + + clientId + + '\'' + + ", sessionId='" + + sessionId + + '\'' + + ", controlConnection=" + + controlConnection + + ", connectedNodes=" + + connectedNodes + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof InsightsStatusData)) { + return false; + } + InsightsStatusData that = (InsightsStatusData) o; + return Objects.equals(clientId, that.clientId) + && Objects.equals(sessionId, that.sessionId) + && Objects.equals(controlConnection, that.controlConnection) + && Objects.equals(connectedNodes, that.connectedNodes); + } + + @Override + public int hashCode() { + return Objects.hash(clientId, sessionId, controlConnection, connectedNodes); + } + + public static InsightsStatusData.Builder builder() { + return new InsightsStatusData.Builder(); + } + + public static class Builder { + private String clientId; + private String sessionId; + private String controlConnection; + private Map connectedNodes; + + public Builder withClientId(String clientId) { + this.clientId = clientId; + return this; + } + + public Builder withSessionId(String id) { + this.sessionId = id; + return this; + } + + public Builder withControlConnection(String controlConnection) { + this.controlConnection = controlConnection; + return this; + } + + public Builder withConnectedNodes(Map connectedNodes) { + this.connectedNodes = connectedNodes; + return this; + } + + public InsightsStatusData build() { + return new InsightsStatusData(clientId, sessionId, controlConnection, connectedNodes); + } + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/LoadBalancingInfo.java b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/LoadBalancingInfo.java new file mode 100644 index 00000000000..594583e3f28 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/LoadBalancingInfo.java @@ -0,0 +1,89 @@ +/* + * 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 com.datastax.dse.driver.internal.core.insights.schema; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Map; +import java.util.Objects; + +public class LoadBalancingInfo { + @JsonProperty("type") + private final String type; + + @JsonProperty("options") + private final Map options; + + @JsonProperty("namespace") + private final String namespace; + + @JsonCreator + public LoadBalancingInfo( + @JsonProperty("type") String type, + @JsonProperty("options") Map options, + @JsonProperty("namespace") String namespace) { + this.type = type; + this.options = options; + this.namespace = namespace; + } + + public String getType() { + return type; + } + + public Map getOptions() { + return options; + } + + public String getNamespace() { + return namespace; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof LoadBalancingInfo)) { + return false; + } + LoadBalancingInfo that = (LoadBalancingInfo) o; + return Objects.equals(type, that.type) + && Objects.equals(options, that.options) + && Objects.equals(namespace, that.namespace); + } + + @Override + public int hashCode() { + return Objects.hash(type, options, namespace); + } + + @Override + public String toString() { + return "LoadBalancingInfo{" + + "type='" + + type + + '\'' + + ", options=" + + options + + ", namespace='" + + namespace + + '\'' + + '}'; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/PoolSizeByHostDistance.java b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/PoolSizeByHostDistance.java new file mode 100644 index 00000000000..07f76a18d40 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/PoolSizeByHostDistance.java @@ -0,0 +1,85 @@ +/* + * 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 com.datastax.dse.driver.internal.core.insights.schema; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; + +public class PoolSizeByHostDistance { + @JsonProperty("local") + private final int local; + + @JsonProperty("remote") + private final int remote; + + @JsonProperty("ignored") + private final int ignored; + + @JsonCreator + public PoolSizeByHostDistance( + @JsonProperty("local") int local, + @JsonProperty("remote") int remote, + @JsonProperty("ignored") int ignored) { + + this.local = local; + this.remote = remote; + this.ignored = ignored; + } + + public int getLocal() { + return local; + } + + public int getRemote() { + return remote; + } + + public int getIgnored() { + return ignored; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof PoolSizeByHostDistance)) { + return false; + } + PoolSizeByHostDistance that = (PoolSizeByHostDistance) o; + return local == that.local && remote == that.remote && ignored == that.ignored; + } + + @Override + public int hashCode() { + return Objects.hash(local, remote, ignored); + } + + @Override + public String toString() { + return "PoolSizeByHostDistance{" + + "local=" + + local + + ", remote=" + + remote + + ", ignored=" + + ignored + + '}'; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/ReconnectionPolicyInfo.java b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/ReconnectionPolicyInfo.java new file mode 100644 index 00000000000..463c23a4325 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/ReconnectionPolicyInfo.java @@ -0,0 +1,90 @@ +/* + * 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 com.datastax.dse.driver.internal.core.insights.schema; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Map; +import java.util.Objects; + +public class ReconnectionPolicyInfo { + @JsonProperty("type") + private final String type; + + @JsonProperty("options") + private final Map options; + + @JsonProperty("namespace") + private final String namespace; + + @JsonCreator + public ReconnectionPolicyInfo( + @JsonProperty("type") String type, + @JsonProperty("options") Map options, + @JsonProperty("namespace") String namespace) { + + this.type = type; + this.options = options; + this.namespace = namespace; + } + + public String getType() { + return type; + } + + public Map getOptions() { + return options; + } + + public String getNamespace() { + return namespace; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof ReconnectionPolicyInfo)) { + return false; + } + ReconnectionPolicyInfo that = (ReconnectionPolicyInfo) o; + return Objects.equals(type, that.type) + && Objects.equals(options, that.options) + && Objects.equals(namespace, that.namespace); + } + + @Override + public int hashCode() { + return Objects.hash(type, options, namespace); + } + + @Override + public String toString() { + return "ReconnectionPolicyInfo{" + + "type='" + + type + + '\'' + + ", options=" + + options + + ", namespace='" + + namespace + + '\'' + + '}'; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/SSL.java b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/SSL.java new file mode 100644 index 00000000000..debcd85c025 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/SSL.java @@ -0,0 +1,63 @@ +/* + * 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 com.datastax.dse.driver.internal.core.insights.schema; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; + +public class SSL { + @JsonProperty("enabled") + private final boolean enabled; + + @JsonProperty("certValidation") + private final boolean certValidation; + + @JsonCreator + public SSL( + @JsonProperty("enabled") boolean enabled, + @JsonProperty("certValidation") boolean certValidation) { + this.enabled = enabled; + this.certValidation = certValidation; + } + + public boolean isEnabled() { + return enabled; + } + + public boolean isCertValidation() { + return certValidation; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof SSL)) { + return false; + } + SSL that = (SSL) o; + return enabled == that.enabled && certValidation == that.certValidation; + } + + @Override + public int hashCode() { + return Objects.hash(enabled, certValidation); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/SessionStateForNode.java b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/SessionStateForNode.java new file mode 100644 index 00000000000..8b50e5b2313 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/SessionStateForNode.java @@ -0,0 +1,74 @@ +/* + * 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 com.datastax.dse.driver.internal.core.insights.schema; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; + +public class SessionStateForNode { + @JsonProperty("connections") + private final Integer connections; + + @JsonProperty("inFlightQueries") + private final Integer inFlightQueries; + + @JsonCreator + public SessionStateForNode( + @JsonProperty("connections") Integer connections, + @JsonProperty("inFlightQueries") Integer inFlightQueries) { + this.connections = connections; + this.inFlightQueries = inFlightQueries; + } + + public Integer getConnections() { + return connections; + } + + public Integer getInFlightQueries() { + return inFlightQueries; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof SessionStateForNode)) { + return false; + } + SessionStateForNode that = (SessionStateForNode) o; + return Objects.equals(connections, that.connections) + && Objects.equals(inFlightQueries, that.inFlightQueries); + } + + @Override + public int hashCode() { + return Objects.hash(connections, inFlightQueries); + } + + @Override + public String toString() { + return "SessionStateForNode{" + + "connections=" + + connections + + ", inFlightQueries=" + + inFlightQueries + + '}'; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/SpecificExecutionProfile.java b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/SpecificExecutionProfile.java new file mode 100644 index 00000000000..58652fdf885 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/SpecificExecutionProfile.java @@ -0,0 +1,133 @@ +/* + * 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 com.datastax.dse.driver.internal.core.insights.schema; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Map; +import java.util.Objects; + +@JsonInclude(JsonInclude.Include.NON_NULL) +public class SpecificExecutionProfile { + @JsonProperty("readTimeout") + private final Integer readTimeout; + + @JsonProperty("loadBalancing") + private final LoadBalancingInfo loadBalancing; + + @JsonProperty("speculativeExecution") + private SpeculativeExecutionInfo speculativeExecution; + + @JsonProperty("consistency") + private final String consistency; + + @JsonProperty("serialConsistency") + private final String serialConsistency; + + @JsonProperty("graphOptions") + private Map graphOptions; + + @JsonCreator + public SpecificExecutionProfile( + @JsonProperty("readTimeout") Integer readTimeoutMillis, + @JsonProperty("loadBalancing") LoadBalancingInfo loadBalancing, + @JsonProperty("speculativeExecution") SpeculativeExecutionInfo speculativeExecutionInfo, + @JsonProperty("consistency") String consistency, + @JsonProperty("serialConsistency") String serialConsistency, + @JsonProperty("graphOptions") Map graphOptions) { + readTimeout = readTimeoutMillis; + this.loadBalancing = loadBalancing; + this.speculativeExecution = speculativeExecutionInfo; + this.consistency = consistency; + this.serialConsistency = serialConsistency; + this.graphOptions = graphOptions; + } + + public Integer getReadTimeout() { + return readTimeout; + } + + public LoadBalancingInfo getLoadBalancing() { + return loadBalancing; + } + + public SpeculativeExecutionInfo getSpeculativeExecution() { + return speculativeExecution; + } + + public String getConsistency() { + return consistency; + } + + public String getSerialConsistency() { + return serialConsistency; + } + + public Map getGraphOptions() { + return graphOptions; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof SpecificExecutionProfile)) { + return false; + } + SpecificExecutionProfile that = (SpecificExecutionProfile) o; + return Objects.equals(readTimeout, that.readTimeout) + && Objects.equals(loadBalancing, that.loadBalancing) + && Objects.equals(speculativeExecution, that.speculativeExecution) + && Objects.equals(consistency, that.consistency) + && Objects.equals(serialConsistency, that.serialConsistency) + && Objects.equals(graphOptions, that.graphOptions); + } + + @Override + public int hashCode() { + return Objects.hash( + readTimeout, + loadBalancing, + speculativeExecution, + consistency, + serialConsistency, + graphOptions); + } + + @Override + public String toString() { + return "SpecificExecutionProfile{" + + "readTimeout=" + + readTimeout + + ", loadBalancing=" + + loadBalancing + + ", speculativeExecution=" + + speculativeExecution + + ", consistency='" + + consistency + + '\'' + + ", serialConsistency='" + + serialConsistency + + '\'' + + ", graphOptions=" + + graphOptions + + '}'; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/SpeculativeExecutionInfo.java b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/SpeculativeExecutionInfo.java new file mode 100644 index 00000000000..779a4ed9e51 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/insights/schema/SpeculativeExecutionInfo.java @@ -0,0 +1,89 @@ +/* + * 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 com.datastax.dse.driver.internal.core.insights.schema; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Map; +import java.util.Objects; + +public class SpeculativeExecutionInfo { + @JsonProperty("type") + private final String type; + + @JsonProperty("options") + private final Map options; + + @JsonProperty("namespace") + private String namespace; + + @JsonCreator + public SpeculativeExecutionInfo( + @JsonProperty("type") String type, + @JsonProperty("options") Map options, + @JsonProperty("namespace") String namespace) { + this.type = type; + this.options = options; + this.namespace = namespace; + } + + public String getType() { + return type; + } + + public Map getOptions() { + return options; + } + + public String getNamespace() { + return namespace; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof SpeculativeExecutionInfo)) { + return false; + } + SpeculativeExecutionInfo that = (SpeculativeExecutionInfo) o; + return Objects.equals(type, that.type) + && Objects.equals(options, that.options) + && Objects.equals(namespace, that.namespace); + } + + @Override + public int hashCode() { + return Objects.hash(type, options, namespace); + } + + @Override + public String toString() { + return "SpeculativeExecutionInfo{" + + "type='" + + type + + '\'' + + ", options=" + + options + + ", namespace='" + + namespace + + '\'' + + '}'; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/loadbalancing/DseDcInferringLoadBalancingPolicy.java b/core/src/main/java/com/datastax/dse/driver/internal/core/loadbalancing/DseDcInferringLoadBalancingPolicy.java new file mode 100644 index 00000000000..501fa263258 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/loadbalancing/DseDcInferringLoadBalancingPolicy.java @@ -0,0 +1,34 @@ +/* + * 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 com.datastax.dse.driver.internal.core.loadbalancing; + +import com.datastax.oss.driver.api.core.context.DriverContext; +import com.datastax.oss.driver.internal.core.loadbalancing.DcInferringLoadBalancingPolicy; +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * @deprecated This class only exists for backward compatibility. It is equivalent to {@link + * DcInferringLoadBalancingPolicy}, which should now be used instead. + */ +@Deprecated +public class DseDcInferringLoadBalancingPolicy extends DcInferringLoadBalancingPolicy { + public DseDcInferringLoadBalancingPolicy( + @NonNull DriverContext context, @NonNull String profileName) { + super(context, profileName); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/loadbalancing/DseLoadBalancingPolicy.java b/core/src/main/java/com/datastax/dse/driver/internal/core/loadbalancing/DseLoadBalancingPolicy.java new file mode 100644 index 00000000000..059a37c4774 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/loadbalancing/DseLoadBalancingPolicy.java @@ -0,0 +1,33 @@ +/* + * 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 com.datastax.dse.driver.internal.core.loadbalancing; + +import com.datastax.oss.driver.api.core.context.DriverContext; +import com.datastax.oss.driver.internal.core.loadbalancing.DefaultLoadBalancingPolicy; +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * @deprecated This class only exists for backward compatibility. It is equivalent to {@link + * DefaultLoadBalancingPolicy}, which should now be used instead. + */ +@Deprecated +public class DseLoadBalancingPolicy extends DefaultLoadBalancingPolicy { + public DseLoadBalancingPolicy(@NonNull DriverContext context, @NonNull String profileName) { + super(context, profileName); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/DefaultDseAggregateMetadata.java b/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/DefaultDseAggregateMetadata.java new file mode 100644 index 00000000000..52a0b846076 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/DefaultDseAggregateMetadata.java @@ -0,0 +1,116 @@ +/* + * 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 com.datastax.dse.driver.internal.core.metadata.schema; + +import com.datastax.dse.driver.api.core.metadata.schema.DseAggregateMetadata; +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.api.core.metadata.schema.FunctionSignature; +import com.datastax.oss.driver.api.core.type.DataType; +import com.datastax.oss.driver.api.core.type.codec.TypeCodec; +import com.datastax.oss.driver.internal.core.metadata.schema.DefaultAggregateMetadata; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.util.Objects; +import java.util.Optional; +import net.jcip.annotations.Immutable; + +@Immutable +public class DefaultDseAggregateMetadata extends DefaultAggregateMetadata + implements DseAggregateMetadata { + + @Nullable private final Boolean deterministic; + + public DefaultDseAggregateMetadata( + @NonNull CqlIdentifier keyspace, + @NonNull FunctionSignature signature, + @Nullable FunctionSignature finalFuncSignature, + @Nullable Object initCond, + @NonNull DataType returnType, + @NonNull FunctionSignature stateFuncSignature, + @NonNull DataType stateType, + @NonNull TypeCodec stateTypeCodec, + @Nullable Boolean deterministic) { + super( + keyspace, + signature, + finalFuncSignature, + initCond, + returnType, + stateFuncSignature, + stateType, + stateTypeCodec); + this.deterministic = deterministic; + } + + @Override + @Deprecated + public boolean isDeterministic() { + return deterministic != null && deterministic; + } + + @Override + @Nullable + public Optional getDeterministic() { + return Optional.ofNullable(deterministic); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } else if (other instanceof DseAggregateMetadata) { + DseAggregateMetadata that = (DseAggregateMetadata) other; + return Objects.equals(this.getKeyspace(), that.getKeyspace()) + && Objects.equals(this.getSignature(), that.getSignature()) + && Objects.equals( + this.getFinalFuncSignature().orElse(null), that.getFinalFuncSignature().orElse(null)) + && Objects.equals(this.getInitCond().orElse(null), that.getInitCond().orElse(null)) + && Objects.equals(this.getReturnType(), that.getReturnType()) + && Objects.equals(this.getStateFuncSignature(), that.getStateFuncSignature()) + && Objects.equals(this.getStateType(), that.getStateType()) + && Objects.equals(this.deterministic, that.getDeterministic().orElse(null)); + } else { + return false; + } + } + + @Override + public int hashCode() { + return Objects.hash( + getKeyspace(), + getSignature(), + getFinalFuncSignature(), + getInitCond(), + getReturnType(), + getStateFuncSignature(), + getStateType(), + deterministic); + } + + @Override + public String toString() { + return "Aggregate Name: " + + getSignature().getName().asCql(false) + + ", Keyspace: " + + getKeyspace().asCql(false) + + ", Return Type: " + + getReturnType().asCql(false, false) + + ", Deterministic: " + + deterministic; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/DefaultDseColumnMetadata.java b/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/DefaultDseColumnMetadata.java new file mode 100644 index 00000000000..2168f20fdc7 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/DefaultDseColumnMetadata.java @@ -0,0 +1,38 @@ +/* + * 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 com.datastax.dse.driver.internal.core.metadata.schema; + +import com.datastax.dse.driver.api.core.metadata.schema.DseColumnMetadata; +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.api.core.type.DataType; +import com.datastax.oss.driver.internal.core.metadata.schema.DefaultColumnMetadata; +import edu.umd.cs.findbugs.annotations.NonNull; +import net.jcip.annotations.Immutable; + +@Immutable +public class DefaultDseColumnMetadata extends DefaultColumnMetadata implements DseColumnMetadata { + + public DefaultDseColumnMetadata( + @NonNull CqlIdentifier keyspace, + @NonNull CqlIdentifier parent, + @NonNull CqlIdentifier name, + @NonNull DataType dataType, + boolean isStatic) { + super(keyspace, parent, name, dataType, isStatic); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/DefaultDseEdgeMetadata.java b/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/DefaultDseEdgeMetadata.java new file mode 100644 index 00000000000..e4de62f294c --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/DefaultDseEdgeMetadata.java @@ -0,0 +1,152 @@ +/* + * 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 com.datastax.dse.driver.internal.core.metadata.schema; + +import com.datastax.dse.driver.api.core.metadata.schema.DseEdgeMetadata; +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.shaded.guava.common.base.Preconditions; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.io.Serializable; +import java.util.List; +import java.util.Objects; + +public class DefaultDseEdgeMetadata implements DseEdgeMetadata, Serializable { + + private static final long serialVersionUID = 1; + + @NonNull private final CqlIdentifier labelName; + + @NonNull private final CqlIdentifier fromTable; + @NonNull private final CqlIdentifier fromLabel; + @NonNull private final List fromPartitionKeyColumns; + @NonNull private final List fromClusteringColumns; + + @NonNull private final CqlIdentifier toTable; + @NonNull private final CqlIdentifier toLabel; + @NonNull private final List toPartitionKeyColumns; + @NonNull private final List toClusteringColumns; + + public DefaultDseEdgeMetadata( + @NonNull CqlIdentifier labelName, + @NonNull CqlIdentifier fromTable, + @NonNull CqlIdentifier fromLabel, + @NonNull List fromPartitionKeyColumns, + @NonNull List fromClusteringColumns, + @NonNull CqlIdentifier toTable, + @NonNull CqlIdentifier toLabel, + @NonNull List toPartitionKeyColumns, + @NonNull List toClusteringColumns) { + this.labelName = Preconditions.checkNotNull(labelName); + this.fromTable = Preconditions.checkNotNull(fromTable); + this.fromLabel = Preconditions.checkNotNull(fromLabel); + this.fromPartitionKeyColumns = Preconditions.checkNotNull(fromPartitionKeyColumns); + this.fromClusteringColumns = Preconditions.checkNotNull(fromClusteringColumns); + this.toTable = Preconditions.checkNotNull(toTable); + this.toLabel = Preconditions.checkNotNull(toLabel); + this.toPartitionKeyColumns = Preconditions.checkNotNull(toPartitionKeyColumns); + this.toClusteringColumns = Preconditions.checkNotNull(toClusteringColumns); + } + + @NonNull + @Override + public CqlIdentifier getLabelName() { + return labelName; + } + + @NonNull + @Override + public CqlIdentifier getFromTable() { + return fromTable; + } + + @NonNull + @Override + public CqlIdentifier getFromLabel() { + return fromLabel; + } + + @NonNull + @Override + public List getFromPartitionKeyColumns() { + return fromPartitionKeyColumns; + } + + @NonNull + @Override + public List getFromClusteringColumns() { + return fromClusteringColumns; + } + + @NonNull + @Override + public CqlIdentifier getToTable() { + return toTable; + } + + @NonNull + @Override + public CqlIdentifier getToLabel() { + return toLabel; + } + + @NonNull + @Override + public List getToPartitionKeyColumns() { + return toPartitionKeyColumns; + } + + @NonNull + @Override + public List getToClusteringColumns() { + return toClusteringColumns; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } else if (other instanceof DseEdgeMetadata) { + DseEdgeMetadata that = (DseEdgeMetadata) other; + return Objects.equals(this.labelName, that.getLabelName()) + && Objects.equals(this.fromTable, that.getFromTable()) + && Objects.equals(this.fromLabel, that.getFromLabel()) + && Objects.equals(this.fromPartitionKeyColumns, that.getFromPartitionKeyColumns()) + && Objects.equals(this.fromClusteringColumns, that.getFromClusteringColumns()) + && Objects.equals(this.toTable, that.getToTable()) + && Objects.equals(this.toLabel, that.getToLabel()) + && Objects.equals(this.toPartitionKeyColumns, that.getToPartitionKeyColumns()) + && Objects.equals(this.toClusteringColumns, that.getToClusteringColumns()); + } else { + return false; + } + } + + @Override + public int hashCode() { + return Objects.hash( + labelName, + fromTable, + fromLabel, + fromPartitionKeyColumns, + fromClusteringColumns, + toTable, + toLabel, + toPartitionKeyColumns, + toClusteringColumns); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/DefaultDseFunctionMetadata.java b/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/DefaultDseFunctionMetadata.java new file mode 100644 index 00000000000..0a94491f1f7 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/DefaultDseFunctionMetadata.java @@ -0,0 +1,147 @@ +/* + * 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 com.datastax.dse.driver.internal.core.metadata.schema; + +import com.datastax.dse.driver.api.core.metadata.schema.DseFunctionMetadata; +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.api.core.metadata.schema.FunctionSignature; +import com.datastax.oss.driver.api.core.type.DataType; +import com.datastax.oss.driver.internal.core.metadata.schema.DefaultFunctionMetadata; +import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableList; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import net.jcip.annotations.Immutable; + +@Immutable +public class DefaultDseFunctionMetadata extends DefaultFunctionMetadata + implements DseFunctionMetadata { + + @Nullable private final Boolean deterministic; + @Nullable private final Monotonicity monotonicity; + @NonNull private final List monotonicArgumentNames; + + public DefaultDseFunctionMetadata( + @NonNull CqlIdentifier keyspace, + @NonNull FunctionSignature signature, + @NonNull List parameterNames, + @NonNull String body, + boolean calledOnNullInput, + @NonNull String language, + @NonNull DataType returnType, + @Nullable Boolean deterministic, + @Nullable Boolean monotonic, + @NonNull List monotonicArgumentNames) { + super(keyspace, signature, parameterNames, body, calledOnNullInput, language, returnType); + // set DSE extension attributes + this.deterministic = deterministic; + this.monotonicity = + monotonic == null + ? null + : monotonic + ? Monotonicity.FULLY_MONOTONIC + : monotonicArgumentNames.isEmpty() + ? Monotonicity.NOT_MONOTONIC + : Monotonicity.PARTIALLY_MONOTONIC; + this.monotonicArgumentNames = ImmutableList.copyOf(monotonicArgumentNames); + } + + @Override + @Deprecated + public boolean isDeterministic() { + return deterministic != null && deterministic; + } + + @Override + public Optional getDeterministic() { + return Optional.ofNullable(deterministic); + } + + @Override + @Deprecated + public boolean isMonotonic() { + return monotonicity == Monotonicity.FULLY_MONOTONIC; + } + + @Override + public Optional getMonotonicity() { + return Optional.ofNullable(monotonicity); + } + + @NonNull + @Override + public List getMonotonicArgumentNames() { + return this.monotonicArgumentNames; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } else if (other instanceof DseFunctionMetadata) { + DseFunctionMetadata that = (DseFunctionMetadata) other; + return Objects.equals(this.getKeyspace(), that.getKeyspace()) + && Objects.equals(this.getSignature(), that.getSignature()) + && Objects.equals(this.getParameterNames(), that.getParameterNames()) + && Objects.equals(this.getBody(), that.getBody()) + && this.isCalledOnNullInput() == that.isCalledOnNullInput() + && Objects.equals(this.getLanguage(), that.getLanguage()) + && Objects.equals(this.getReturnType(), that.getReturnType()) + && Objects.equals(this.deterministic, that.getDeterministic().orElse(null)) + && this.monotonicity == that.getMonotonicity().orElse(null) + && Objects.equals(this.monotonicArgumentNames, that.getMonotonicArgumentNames()); + } else { + return false; + } + } + + @Override + public int hashCode() { + return Objects.hash( + getKeyspace(), + getSignature(), + getParameterNames(), + getBody(), + isCalledOnNullInput(), + getLanguage(), + getReturnType(), + deterministic, + monotonicity, + monotonicArgumentNames); + } + + @Override + public String toString() { + return "Function Name: " + + this.getSignature().getName().asCql(false) + + ", Keyspace: " + + this.getKeyspace().asCql(false) + + ", Language: " + + this.getLanguage() + + ", Return Type: " + + getReturnType().asCql(false, false) + + ", Deterministic: " + + this.deterministic + + ", Monotonicity: " + + this.monotonicity + + ", Monotonic On: " + + (this.monotonicArgumentNames.isEmpty() ? "" : this.monotonicArgumentNames.get(0)); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/DefaultDseIndexMetadata.java b/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/DefaultDseIndexMetadata.java new file mode 100644 index 00000000000..c66d7934151 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/DefaultDseIndexMetadata.java @@ -0,0 +1,40 @@ +/* + * 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 com.datastax.dse.driver.internal.core.metadata.schema; + +import com.datastax.dse.driver.api.core.metadata.schema.DseIndexMetadata; +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.api.core.metadata.schema.IndexKind; +import com.datastax.oss.driver.internal.core.metadata.schema.DefaultIndexMetadata; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.Map; +import net.jcip.annotations.Immutable; + +@Immutable +public class DefaultDseIndexMetadata extends DefaultIndexMetadata implements DseIndexMetadata { + + public DefaultDseIndexMetadata( + @NonNull CqlIdentifier keyspace, + @NonNull CqlIdentifier table, + @NonNull CqlIdentifier name, + @NonNull IndexKind kind, + @NonNull String target, + @NonNull Map options) { + super(keyspace, table, name, kind, target, options); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/DefaultDseKeyspaceMetadata.java b/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/DefaultDseKeyspaceMetadata.java new file mode 100644 index 00000000000..8e54c9082e1 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/DefaultDseKeyspaceMetadata.java @@ -0,0 +1,183 @@ +/* + * 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 com.datastax.dse.driver.internal.core.metadata.schema; + +import com.datastax.dse.driver.api.core.metadata.schema.DseGraphKeyspaceMetadata; +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.api.core.metadata.schema.AggregateMetadata; +import com.datastax.oss.driver.api.core.metadata.schema.FunctionMetadata; +import com.datastax.oss.driver.api.core.metadata.schema.FunctionSignature; +import com.datastax.oss.driver.api.core.metadata.schema.TableMetadata; +import com.datastax.oss.driver.api.core.metadata.schema.ViewMetadata; +import com.datastax.oss.driver.api.core.type.UserDefinedType; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.io.Serializable; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import net.jcip.annotations.Immutable; + +@Immutable +public class DefaultDseKeyspaceMetadata implements DseGraphKeyspaceMetadata, Serializable { + + private static final long serialVersionUID = 1; + + @NonNull private final CqlIdentifier name; + private final boolean durableWrites; + private final boolean virtual; + @Nullable private final String graphEngine; + @NonNull private final Map replication; + @NonNull private final Map types; + @NonNull private final Map tables; + @NonNull private final Map views; + @NonNull private final Map functions; + @NonNull private final Map aggregates; + + public DefaultDseKeyspaceMetadata( + @NonNull CqlIdentifier name, + boolean durableWrites, + boolean virtual, + @Nullable String graphEngine, + @NonNull Map replication, + @NonNull Map types, + @NonNull Map tables, + @NonNull Map views, + @NonNull Map functions, + @NonNull Map aggregates) { + this.name = name; + this.durableWrites = durableWrites; + this.virtual = virtual; + this.graphEngine = graphEngine; + this.replication = replication; + this.types = types; + this.tables = tables; + this.views = views; + this.functions = functions; + this.aggregates = aggregates; + } + + @NonNull + @Override + public CqlIdentifier getName() { + return name; + } + + @Override + public boolean isDurableWrites() { + return durableWrites; + } + + @Override + public boolean isVirtual() { + return virtual; + } + + @NonNull + @Override + public Optional getGraphEngine() { + return Optional.ofNullable(graphEngine); + } + + @NonNull + @Override + public Map getReplication() { + return replication; + } + + @NonNull + @Override + public Map getUserDefinedTypes() { + return types; + } + + @NonNull + @Override + public Map getTables() { + return tables; + } + + @NonNull + @Override + public Map getViews() { + return views; + } + + @NonNull + @Override + public Map getFunctions() { + return functions; + } + + @NonNull + @Override + public Map getAggregates() { + return aggregates; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } else if (other instanceof DseGraphKeyspaceMetadata) { + DseGraphKeyspaceMetadata that = (DseGraphKeyspaceMetadata) other; + return Objects.equals(this.name, that.getName()) + && this.durableWrites == that.isDurableWrites() + && this.virtual == that.isVirtual() + && Objects.equals(this.graphEngine, that.getGraphEngine().orElse(null)) + && Objects.equals(this.replication, that.getReplication()) + && Objects.equals(this.types, that.getUserDefinedTypes()) + && Objects.equals(this.tables, that.getTables()) + && Objects.equals(this.views, that.getViews()) + && Objects.equals(this.functions, that.getFunctions()) + && Objects.equals(this.aggregates, that.getAggregates()); + } else { + return false; + } + } + + @Override + public int hashCode() { + return Objects.hash( + name, + durableWrites, + virtual, + graphEngine, + replication, + types, + tables, + views, + functions, + aggregates); + } + + @Override + public boolean shallowEquals(Object other) { + if (other == this) { + return true; + } else if (other instanceof DseGraphKeyspaceMetadata) { + DseGraphKeyspaceMetadata that = (DseGraphKeyspaceMetadata) other; + return Objects.equals(this.name, that.getName()) + && this.durableWrites == that.isDurableWrites() + && Objects.equals(this.graphEngine, that.getGraphEngine().orElse(null)) + && Objects.equals(this.replication, that.getReplication()); + } else { + return false; + } + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/DefaultDseTableMetadata.java b/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/DefaultDseTableMetadata.java new file mode 100644 index 00000000000..f8fb8cc10d1 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/DefaultDseTableMetadata.java @@ -0,0 +1,190 @@ +/* + * 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 com.datastax.dse.driver.internal.core.metadata.schema; + +import com.datastax.dse.driver.api.core.metadata.schema.DseEdgeMetadata; +import com.datastax.dse.driver.api.core.metadata.schema.DseGraphTableMetadata; +import com.datastax.dse.driver.api.core.metadata.schema.DseVertexMetadata; +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.api.core.metadata.schema.ClusteringOrder; +import com.datastax.oss.driver.api.core.metadata.schema.ColumnMetadata; +import com.datastax.oss.driver.api.core.metadata.schema.IndexMetadata; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.io.Serializable; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.UUID; +import net.jcip.annotations.Immutable; + +@Immutable +public class DefaultDseTableMetadata implements DseGraphTableMetadata, Serializable { + + private static final long serialVersionUID = 1; + + @NonNull private final CqlIdentifier keyspace; + @NonNull private final CqlIdentifier name; + // null for virtual tables + @Nullable private final UUID id; + private final boolean compactStorage; + private final boolean virtual; + @NonNull private final List partitionKey; + @NonNull private final Map clusteringColumns; + @NonNull private final Map columns; + @NonNull private final Map options; + @NonNull private final Map indexes; + @Nullable private final DseVertexMetadata vertex; + @Nullable private final DseEdgeMetadata edge; + + public DefaultDseTableMetadata( + @NonNull CqlIdentifier keyspace, + @NonNull CqlIdentifier name, + @Nullable UUID id, + boolean compactStorage, + boolean virtual, + @NonNull List partitionKey, + @NonNull Map clusteringColumns, + @NonNull Map columns, + @NonNull Map options, + @NonNull Map indexes, + @Nullable DseVertexMetadata vertex, + @Nullable DseEdgeMetadata edge) { + this.keyspace = keyspace; + this.name = name; + this.id = id; + this.compactStorage = compactStorage; + this.virtual = virtual; + this.partitionKey = partitionKey; + this.clusteringColumns = clusteringColumns; + this.columns = columns; + this.options = options; + this.indexes = indexes; + this.vertex = vertex; + this.edge = edge; + } + + @NonNull + @Override + public CqlIdentifier getKeyspace() { + return keyspace; + } + + @NonNull + @Override + public CqlIdentifier getName() { + return name; + } + + @NonNull + @Override + public Optional getId() { + return Optional.ofNullable(id); + } + + @Override + public boolean isCompactStorage() { + return compactStorage; + } + + @Override + public boolean isVirtual() { + return virtual; + } + + @NonNull + @Override + public List getPartitionKey() { + return partitionKey; + } + + @NonNull + @Override + public Map getClusteringColumns() { + return clusteringColumns; + } + + @NonNull + @Override + public Map getColumns() { + return columns; + } + + @NonNull + @Override + public Map getOptions() { + return options; + } + + @NonNull + @Override + public Map getIndexes() { + return indexes; + } + + @NonNull + @Override + public Optional getVertex() { + return Optional.ofNullable(vertex); + } + + @NonNull + @Override + public Optional getEdge() { + return Optional.ofNullable(edge); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } else if (other instanceof DseGraphTableMetadata) { + DseGraphTableMetadata that = (DseGraphTableMetadata) other; + return Objects.equals(this.keyspace, that.getKeyspace()) + && Objects.equals(this.name, that.getName()) + && Objects.equals(this.id, that.getId().orElse(null)) + && this.compactStorage == that.isCompactStorage() + && this.virtual == that.isVirtual() + && Objects.equals(this.partitionKey, that.getPartitionKey()) + && Objects.equals(this.clusteringColumns, that.getClusteringColumns()) + && Objects.equals(this.columns, that.getColumns()) + && Objects.equals(this.indexes, that.getIndexes()) + && Objects.equals(this.vertex, that.getVertex().orElse(null)) + && Objects.equals(this.edge, that.getEdge().orElse(null)); + } else { + return false; + } + } + + @Override + public int hashCode() { + return Objects.hash( + keyspace, + name, + id, + compactStorage, + virtual, + partitionKey, + clusteringColumns, + columns, + indexes, + vertex, + edge); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/DefaultDseVertexMetadata.java b/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/DefaultDseVertexMetadata.java new file mode 100644 index 00000000000..05ba2823704 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/DefaultDseVertexMetadata.java @@ -0,0 +1,59 @@ +/* + * 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 com.datastax.dse.driver.internal.core.metadata.schema; + +import com.datastax.dse.driver.api.core.metadata.schema.DseVertexMetadata; +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.shaded.guava.common.base.Preconditions; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.io.Serializable; +import java.util.Objects; + +public class DefaultDseVertexMetadata implements DseVertexMetadata, Serializable { + + private static final long serialVersionUID = 1; + + @NonNull private final CqlIdentifier labelName; + + public DefaultDseVertexMetadata(@NonNull CqlIdentifier labelName) { + this.labelName = Preconditions.checkNotNull(labelName); + } + + @NonNull + @Override + public CqlIdentifier getLabelName() { + return labelName; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } else if (other instanceof DefaultDseVertexMetadata) { + DefaultDseVertexMetadata that = (DefaultDseVertexMetadata) other; + return Objects.equals(this.labelName, that.getLabelName()); + } else { + return false; + } + } + + @Override + public int hashCode() { + return labelName.hashCode(); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/DefaultDseViewMetadata.java b/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/DefaultDseViewMetadata.java new file mode 100644 index 00000000000..f04b7640041 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/DefaultDseViewMetadata.java @@ -0,0 +1,169 @@ +/* + * 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 com.datastax.dse.driver.internal.core.metadata.schema; + +import com.datastax.dse.driver.api.core.metadata.schema.DseViewMetadata; +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.api.core.metadata.schema.ClusteringOrder; +import com.datastax.oss.driver.api.core.metadata.schema.ColumnMetadata; +import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableList; +import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableMap; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.io.Serializable; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.UUID; +import net.jcip.annotations.Immutable; + +@Immutable +public class DefaultDseViewMetadata implements DseViewMetadata, Serializable { + + private static final long serialVersionUID = 1; + + @NonNull private final CqlIdentifier keyspace; + @NonNull private final CqlIdentifier name; + @NonNull private final CqlIdentifier baseTable; + private final boolean includesAllColumns; + @Nullable private final String whereClause; + @NonNull private final UUID id; + @NonNull private final ImmutableList partitionKey; + @NonNull private final ImmutableMap clusteringColumns; + @NonNull private final ImmutableMap columns; + @NonNull private final Map options; + + public DefaultDseViewMetadata( + @NonNull CqlIdentifier keyspace, + @NonNull CqlIdentifier name, + @NonNull CqlIdentifier baseTable, + boolean includesAllColumns, + @Nullable String whereClause, + @NonNull UUID id, + @NonNull ImmutableList partitionKey, + @NonNull ImmutableMap clusteringColumns, + @NonNull ImmutableMap columns, + @NonNull Map options) { + this.keyspace = keyspace; + this.name = name; + this.baseTable = baseTable; + this.includesAllColumns = includesAllColumns; + this.whereClause = whereClause; + this.id = id; + this.partitionKey = partitionKey; + this.clusteringColumns = clusteringColumns; + this.columns = columns; + this.options = options; + } + + @NonNull + @Override + public CqlIdentifier getKeyspace() { + return keyspace; + } + + @NonNull + @Override + public CqlIdentifier getName() { + return name; + } + + @NonNull + @Override + public Optional getId() { + return Optional.of(id); + } + + @NonNull + @Override + public CqlIdentifier getBaseTable() { + return baseTable; + } + + @Override + public boolean includesAllColumns() { + return includesAllColumns; + } + + @NonNull + @Override + public Optional getWhereClause() { + return Optional.ofNullable(whereClause); + } + + @NonNull + @Override + public List getPartitionKey() { + return partitionKey; + } + + @NonNull + @Override + public Map getClusteringColumns() { + return clusteringColumns; + } + + @NonNull + @Override + public Map getColumns() { + return columns; + } + + @NonNull + @Override + public Map getOptions() { + return options; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } else if (other instanceof DseViewMetadata) { + DseViewMetadata that = (DseViewMetadata) other; + return Objects.equals(this.keyspace, that.getKeyspace()) + && Objects.equals(this.name, that.getName()) + && Objects.equals(this.baseTable, that.getBaseTable()) + && this.includesAllColumns == that.includesAllColumns() + && Objects.equals(this.whereClause, that.getWhereClause().orElse(null)) + && Objects.equals(Optional.of(this.id), that.getId()) + && Objects.equals(this.partitionKey, that.getPartitionKey()) + && Objects.equals(this.clusteringColumns, that.getClusteringColumns()) + && Objects.equals(this.columns, that.getColumns()) + && Objects.equals(this.options, that.getOptions()); + } else { + return false; + } + } + + @Override + public int hashCode() { + return Objects.hash( + keyspace, + name, + baseTable, + includesAllColumns, + whereClause, + id, + partitionKey, + clusteringColumns, + columns, + options); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/ScriptHelper.java b/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/ScriptHelper.java new file mode 100644 index 00000000000..64f6cac19f0 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/ScriptHelper.java @@ -0,0 +1,56 @@ +/* + * 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 com.datastax.dse.driver.internal.core.metadata.schema; + +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.internal.core.metadata.schema.ScriptBuilder; +import java.util.List; + +public class ScriptHelper { + + public static void appendEdgeSide( + ScriptBuilder builder, + CqlIdentifier table, + CqlIdentifier label, + List partitionKeyColumns, + List clusteringColumns, + String keyword) { + builder.append(" ").append(keyword).append(label).append("("); + + if (partitionKeyColumns.size() == 1) { // PRIMARY KEY (k + builder.append(partitionKeyColumns.get(0)); + } else { // PRIMARY KEY ((k1, k2) + builder.append("("); + boolean first = true; + for (CqlIdentifier pkColumn : partitionKeyColumns) { + if (first) { + first = false; + } else { + builder.append(", "); + } + builder.append(pkColumn); + } + builder.append(")"); + } + // PRIMARY KEY (, cc1, cc2, cc3) + for (CqlIdentifier clusteringColumn : clusteringColumns) { + builder.append(", ").append(clusteringColumn); + } + builder.append(")"); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/parsing/DseAggregateParser.java b/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/parsing/DseAggregateParser.java new file mode 100644 index 00000000000..37a7a2768c2 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/parsing/DseAggregateParser.java @@ -0,0 +1,63 @@ +/* + * 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 com.datastax.dse.driver.internal.core.metadata.schema.parsing; + +import com.datastax.dse.driver.api.core.metadata.schema.DseAggregateMetadata; +import com.datastax.dse.driver.internal.core.metadata.schema.DefaultDseAggregateMetadata; +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.api.core.metadata.schema.AggregateMetadata; +import com.datastax.oss.driver.api.core.type.UserDefinedType; +import com.datastax.oss.driver.internal.core.adminrequest.AdminRow; +import com.datastax.oss.driver.internal.core.context.InternalDriverContext; +import com.datastax.oss.driver.internal.core.metadata.schema.parsing.AggregateParser; +import com.datastax.oss.driver.internal.core.metadata.schema.parsing.DataTypeParser; +import java.util.Map; +import net.jcip.annotations.ThreadSafe; + +@ThreadSafe +public class DseAggregateParser { + + private final AggregateParser aggregateParser; + private final InternalDriverContext context; + + public DseAggregateParser(DataTypeParser dataTypeParser, InternalDriverContext context) { + this.aggregateParser = new AggregateParser(dataTypeParser, context); + this.context = context; + } + + public DseAggregateMetadata parseAggregate( + AdminRow row, + CqlIdentifier keyspaceId, + Map userDefinedTypes) { + AggregateMetadata aggregate = aggregateParser.parseAggregate(row, keyspaceId, userDefinedTypes); + // parse the DSE extended columns + final Boolean deterministic = + row.contains("deterministic") ? row.getBoolean("deterministic") : null; + + return new DefaultDseAggregateMetadata( + aggregate.getKeyspace(), + aggregate.getSignature(), + aggregate.getFinalFuncSignature().orElse(null), + aggregate.getInitCond().orElse(null), + aggregate.getReturnType(), + aggregate.getStateFuncSignature(), + aggregate.getStateType(), + context.getCodecRegistry().codecFor(aggregate.getStateType()), + deterministic); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/parsing/DseFunctionParser.java b/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/parsing/DseFunctionParser.java new file mode 100644 index 00000000000..0d88bce8740 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/parsing/DseFunctionParser.java @@ -0,0 +1,73 @@ +/* + * 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 com.datastax.dse.driver.internal.core.metadata.schema.parsing; + +import com.datastax.dse.driver.api.core.metadata.schema.DseFunctionMetadata; +import com.datastax.dse.driver.internal.core.metadata.schema.DefaultDseFunctionMetadata; +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.api.core.metadata.schema.FunctionMetadata; +import com.datastax.oss.driver.api.core.type.UserDefinedType; +import com.datastax.oss.driver.internal.core.adminrequest.AdminRow; +import com.datastax.oss.driver.internal.core.context.InternalDriverContext; +import com.datastax.oss.driver.internal.core.metadata.schema.parsing.DataTypeParser; +import com.datastax.oss.driver.internal.core.metadata.schema.parsing.FunctionParser; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import net.jcip.annotations.ThreadSafe; + +@ThreadSafe +public class DseFunctionParser { + + private final FunctionParser functionParser; + + public DseFunctionParser(DataTypeParser dataTypeParser, InternalDriverContext context) { + this.functionParser = new FunctionParser(dataTypeParser, context); + } + + public DseFunctionMetadata parseFunction( + AdminRow row, + CqlIdentifier keyspaceId, + Map userDefinedTypes) { + FunctionMetadata function = functionParser.parseFunction(row, keyspaceId, userDefinedTypes); + // parse the DSE extended columns + final Boolean deterministic = + row.contains("deterministic") ? row.getBoolean("deterministic") : null; + final Boolean monotonic = row.contains("monotonic") ? row.getBoolean("monotonic") : null; + // stream the list of strings into a list of CqlIdentifiers + final List monotonicOn = + row.contains("monotonic_on") + ? row.getListOfString("monotonic_on").stream() + .map(CqlIdentifier::fromInternal) + .collect(Collectors.toList()) + : Collections.emptyList(); + + return new DefaultDseFunctionMetadata( + function.getKeyspace(), + function.getSignature(), + function.getParameterNames(), + function.getBody(), + function.isCalledOnNullInput(), + function.getLanguage(), + function.getReturnType(), + deterministic, + monotonic, + monotonicOn); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/parsing/DseSchemaParser.java b/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/parsing/DseSchemaParser.java new file mode 100644 index 00000000000..ca7fb74a746 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/parsing/DseSchemaParser.java @@ -0,0 +1,237 @@ +/* + * 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 com.datastax.dse.driver.internal.core.metadata.schema.parsing; + +import com.datastax.dse.driver.api.core.metadata.schema.DseAggregateMetadata; +import com.datastax.dse.driver.api.core.metadata.schema.DseFunctionMetadata; +import com.datastax.dse.driver.api.core.metadata.schema.DseKeyspaceMetadata; +import com.datastax.dse.driver.api.core.metadata.schema.DseTableMetadata; +import com.datastax.dse.driver.api.core.metadata.schema.DseViewMetadata; +import com.datastax.dse.driver.internal.core.metadata.schema.DefaultDseKeyspaceMetadata; +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.api.core.metadata.schema.AggregateMetadata; +import com.datastax.oss.driver.api.core.metadata.schema.FunctionMetadata; +import com.datastax.oss.driver.api.core.metadata.schema.FunctionSignature; +import com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata; +import com.datastax.oss.driver.api.core.metadata.schema.TableMetadata; +import com.datastax.oss.driver.api.core.metadata.schema.ViewMetadata; +import com.datastax.oss.driver.api.core.type.UserDefinedType; +import com.datastax.oss.driver.internal.core.adminrequest.AdminRow; +import com.datastax.oss.driver.internal.core.context.InternalDriverContext; +import com.datastax.oss.driver.internal.core.metadata.schema.parsing.CassandraSchemaParser; +import com.datastax.oss.driver.internal.core.metadata.schema.parsing.SchemaParser; +import com.datastax.oss.driver.internal.core.metadata.schema.parsing.SimpleJsonParser; +import com.datastax.oss.driver.internal.core.metadata.schema.parsing.UserDefinedTypeParser; +import com.datastax.oss.driver.internal.core.metadata.schema.queries.SchemaRows; +import com.datastax.oss.driver.internal.core.metadata.schema.refresh.SchemaRefresh; +import com.datastax.oss.driver.internal.core.util.NanoTime; +import com.datastax.oss.driver.shaded.guava.common.base.MoreObjects; +import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableMap; +import com.datastax.oss.driver.shaded.guava.common.collect.Multimap; +import java.util.Collections; +import java.util.Map; +import net.jcip.annotations.ThreadSafe; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Default parser implementation for DSE. + * + *

For modularity, the code for each element row is split into separate classes (schema stuff is + * not on the hot path, so creating a few extra objects doesn't matter). + */ +@ThreadSafe +public class DseSchemaParser implements SchemaParser { + + private static final Logger LOG = LoggerFactory.getLogger(CassandraSchemaParser.class); + + private final SchemaRows rows; + private final UserDefinedTypeParser userDefinedTypeParser; + private final DseTableParser tableParser; + private final DseViewParser viewParser; + private final DseFunctionParser functionParser; + private final DseAggregateParser aggregateParser; + private final String logPrefix; + private final long startTimeNs = System.nanoTime(); + + public DseSchemaParser(SchemaRows rows, InternalDriverContext context) { + this.rows = rows; + this.logPrefix = context.getSessionName(); + + this.userDefinedTypeParser = new UserDefinedTypeParser(rows.dataTypeParser(), context); + this.tableParser = new DseTableParser(rows, context); + this.viewParser = new DseViewParser(rows, context); + this.functionParser = new DseFunctionParser(rows.dataTypeParser(), context); + this.aggregateParser = new DseAggregateParser(rows.dataTypeParser(), context); + } + + @Override + public SchemaRefresh parse() { + ImmutableMap.Builder keyspacesBuilder = ImmutableMap.builder(); + for (AdminRow row : rows.keyspaces()) { + DseKeyspaceMetadata keyspace = parseKeyspace(row); + keyspacesBuilder.put(keyspace.getName(), keyspace); + } + for (AdminRow row : rows.virtualKeyspaces()) { + DseKeyspaceMetadata keyspace = parseVirtualKeyspace(row); + keyspacesBuilder.put(keyspace.getName(), keyspace); + } + SchemaRefresh refresh = new SchemaRefresh(keyspacesBuilder.build()); + LOG.debug("[{}] Schema parsing took {}", logPrefix, NanoTime.formatTimeSince(startTimeNs)); + return refresh; + } + + private DseKeyspaceMetadata parseKeyspace(AdminRow keyspaceRow) { + + // Cassandra <= 2.2 + // CREATE TABLE system.schema_keyspaces ( + // keyspace_name text PRIMARY KEY, + // durable_writes boolean, + // strategy_class text, + // strategy_options text + // ) + // + // Cassandra >= 3.0: + // CREATE TABLE system_schema.keyspaces ( + // keyspace_name text PRIMARY KEY, + // durable_writes boolean, + // replication frozen> + // ) + // + // DSE >= 6.8: same as Cassandra 3 + graph_engine text + CqlIdentifier keyspaceId = CqlIdentifier.fromInternal(keyspaceRow.getString("keyspace_name")); + boolean durableWrites = + MoreObjects.firstNonNull(keyspaceRow.getBoolean("durable_writes"), false); + String graphEngine = keyspaceRow.getString("graph_engine"); + + Map replicationOptions; + if (keyspaceRow.contains("strategy_class")) { + String strategyClass = keyspaceRow.getString("strategy_class"); + Map strategyOptions = + SimpleJsonParser.parseStringMap(keyspaceRow.getString("strategy_options")); + replicationOptions = + ImmutableMap.builder() + .putAll(strategyOptions) + .put("class", strategyClass) + .build(); + } else { + replicationOptions = keyspaceRow.getMapOfStringToString("replication"); + } + + Map types = parseTypes(keyspaceId); + + return new DefaultDseKeyspaceMetadata( + keyspaceId, + durableWrites, + false, + graphEngine, + replicationOptions, + types, + parseTables(keyspaceId, types), + parseViews(keyspaceId, types), + parseFunctions(keyspaceId, types), + parseAggregates(keyspaceId, types)); + } + + private Map parseTypes(CqlIdentifier keyspaceId) { + return userDefinedTypeParser.parse(rows.types().get(keyspaceId), keyspaceId); + } + + private Map parseTables( + CqlIdentifier keyspaceId, Map types) { + ImmutableMap.Builder tablesBuilder = ImmutableMap.builder(); + Multimap vertices = rows.vertices().get(keyspaceId); + Multimap edges = rows.edges().get(keyspaceId); + for (AdminRow tableRow : rows.tables().get(keyspaceId)) { + DseTableMetadata table = tableParser.parseTable(tableRow, keyspaceId, types, vertices, edges); + if (table != null) { + tablesBuilder.put(table.getName(), table); + } + } + return tablesBuilder.build(); + } + + private Map parseViews( + CqlIdentifier keyspaceId, Map types) { + ImmutableMap.Builder viewsBuilder = ImmutableMap.builder(); + for (AdminRow viewRow : rows.views().get(keyspaceId)) { + DseViewMetadata view = viewParser.parseView(viewRow, keyspaceId, types); + if (view != null) { + viewsBuilder.put(view.getName(), view); + } + } + return viewsBuilder.build(); + } + + private Map parseFunctions( + CqlIdentifier keyspaceId, Map types) { + ImmutableMap.Builder functionsBuilder = + ImmutableMap.builder(); + for (AdminRow functionRow : rows.functions().get(keyspaceId)) { + DseFunctionMetadata function = functionParser.parseFunction(functionRow, keyspaceId, types); + if (function != null) { + functionsBuilder.put(function.getSignature(), function); + } + } + return functionsBuilder.build(); + } + + private Map parseAggregates( + CqlIdentifier keyspaceId, Map types) { + ImmutableMap.Builder aggregatesBuilder = + ImmutableMap.builder(); + for (AdminRow aggregateRow : rows.aggregates().get(keyspaceId)) { + DseAggregateMetadata aggregate = + aggregateParser.parseAggregate(aggregateRow, keyspaceId, types); + if (aggregate != null) { + aggregatesBuilder.put(aggregate.getSignature(), aggregate); + } + } + return aggregatesBuilder.build(); + } + + private DseKeyspaceMetadata parseVirtualKeyspace(AdminRow keyspaceRow) { + + CqlIdentifier keyspaceId = CqlIdentifier.fromInternal(keyspaceRow.getString("keyspace_name")); + boolean durableWrites = + MoreObjects.firstNonNull(keyspaceRow.getBoolean("durable_writes"), false); + + return new DefaultDseKeyspaceMetadata( + keyspaceId, + durableWrites, + true, + null, + Collections.emptyMap(), + Collections.emptyMap(), + parseVirtualTables(keyspaceId), + Collections.emptyMap(), + Collections.emptyMap(), + Collections.emptyMap()); + } + + private Map parseVirtualTables(CqlIdentifier keyspaceId) { + ImmutableMap.Builder tablesBuilder = ImmutableMap.builder(); + for (AdminRow tableRow : rows.virtualTables().get(keyspaceId)) { + DseTableMetadata table = tableParser.parseVirtualTable(tableRow, keyspaceId); + if (table != null) { + tablesBuilder.put(table.getName(), table); + } + } + return tablesBuilder.build(); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/parsing/DseTableParser.java b/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/parsing/DseTableParser.java new file mode 100644 index 00000000000..7fd4a5f0167 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/parsing/DseTableParser.java @@ -0,0 +1,425 @@ +/* + * 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 com.datastax.dse.driver.internal.core.metadata.schema.parsing; + +import com.datastax.dse.driver.api.core.metadata.schema.DseColumnMetadata; +import com.datastax.dse.driver.api.core.metadata.schema.DseEdgeMetadata; +import com.datastax.dse.driver.api.core.metadata.schema.DseIndexMetadata; +import com.datastax.dse.driver.api.core.metadata.schema.DseTableMetadata; +import com.datastax.dse.driver.api.core.metadata.schema.DseVertexMetadata; +import com.datastax.dse.driver.internal.core.metadata.schema.DefaultDseColumnMetadata; +import com.datastax.dse.driver.internal.core.metadata.schema.DefaultDseEdgeMetadata; +import com.datastax.dse.driver.internal.core.metadata.schema.DefaultDseIndexMetadata; +import com.datastax.dse.driver.internal.core.metadata.schema.DefaultDseTableMetadata; +import com.datastax.dse.driver.internal.core.metadata.schema.DefaultDseVertexMetadata; +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.api.core.metadata.schema.ClusteringOrder; +import com.datastax.oss.driver.api.core.metadata.schema.ColumnMetadata; +import com.datastax.oss.driver.api.core.metadata.schema.IndexKind; +import com.datastax.oss.driver.api.core.metadata.schema.IndexMetadata; +import com.datastax.oss.driver.api.core.type.DataType; +import com.datastax.oss.driver.api.core.type.ListType; +import com.datastax.oss.driver.api.core.type.MapType; +import com.datastax.oss.driver.api.core.type.SetType; +import com.datastax.oss.driver.api.core.type.UserDefinedType; +import com.datastax.oss.driver.internal.core.CqlIdentifiers; +import com.datastax.oss.driver.internal.core.adminrequest.AdminRow; +import com.datastax.oss.driver.internal.core.context.InternalDriverContext; +import com.datastax.oss.driver.internal.core.metadata.schema.parsing.DataTypeClassNameCompositeParser; +import com.datastax.oss.driver.internal.core.metadata.schema.parsing.RawColumn; +import com.datastax.oss.driver.internal.core.metadata.schema.parsing.RelationParser; +import com.datastax.oss.driver.internal.core.metadata.schema.parsing.TableParser; +import com.datastax.oss.driver.internal.core.metadata.schema.queries.SchemaRows; +import com.datastax.oss.driver.internal.core.util.Loggers; +import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableList; +import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableMap; +import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableMultimap; +import com.datastax.oss.driver.shaded.guava.common.collect.Multimap; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import net.jcip.annotations.ThreadSafe; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@ThreadSafe +public class DseTableParser extends RelationParser { + + private static final Logger LOG = LoggerFactory.getLogger(TableParser.class); + + public DseTableParser(SchemaRows rows, InternalDriverContext context) { + super(rows, context); + } + + public DseTableMetadata parseTable( + AdminRow tableRow, + CqlIdentifier keyspaceId, + Map userTypes, + Multimap vertices, + Multimap edges) { + // Cassandra <= 2.2: + // CREATE TABLE system.schema_columnfamilies ( + // keyspace_name text, + // columnfamily_name text, + // bloom_filter_fp_chance double, + // caching text, + // cf_id uuid, + // column_aliases text, (2.1 only) + // comment text, + // compaction_strategy_class text, + // compaction_strategy_options text, + // comparator text, + // compression_parameters text, + // default_time_to_live int, + // default_validator text, + // dropped_columns map, + // gc_grace_seconds int, + // index_interval int, + // is_dense boolean, (2.1 only) + // key_aliases text, (2.1 only) + // key_validator text, + // local_read_repair_chance double, + // max_compaction_threshold int, + // max_index_interval int, + // memtable_flush_period_in_ms int, + // min_compaction_threshold int, + // min_index_interval int, + // read_repair_chance double, + // speculative_retry text, + // subcomparator text, + // type text, + // value_alias text, (2.1 only) + // PRIMARY KEY (keyspace_name, columnfamily_name) + // ) WITH CLUSTERING ORDER BY (columnfamily_name ASC) + // + // Cassandra 3.0: + // CREATE TABLE system_schema.tables ( + // keyspace_name text, + // table_name text, + // bloom_filter_fp_chance double, + // caching frozen>, + // cdc boolean, + // comment text, + // compaction frozen>, + // compression frozen>, + // crc_check_chance double, + // dclocal_read_repair_chance double, + // default_time_to_live int, + // extensions frozen>, + // flags frozen>, + // gc_grace_seconds int, + // id uuid, + // max_index_interval int, + // memtable_flush_period_in_ms int, + // min_index_interval int, + // read_repair_chance double, + // speculative_retry text, + // PRIMARY KEY (keyspace_name, table_name) + // ) WITH CLUSTERING ORDER BY (table_name ASC) + CqlIdentifier tableId = + CqlIdentifier.fromInternal( + tableRow.getString( + tableRow.contains("table_name") ? "table_name" : "columnfamily_name")); + + UUID uuid = tableRow.contains("id") ? tableRow.getUuid("id") : tableRow.getUuid("cf_id"); + + List rawColumns = + RawColumn.toRawColumns( + rows.columns().getOrDefault(keyspaceId, ImmutableMultimap.of()).get(tableId)); + if (rawColumns.isEmpty()) { + LOG.warn( + "[{}] Processing TABLE refresh for {}.{} but found no matching rows, skipping", + logPrefix, + keyspaceId, + tableId); + return null; + } + + boolean isCompactStorage; + if (tableRow.contains("flags")) { + Set flags = tableRow.getSetOfString("flags"); + boolean isDense = flags.contains("dense"); + boolean isSuper = flags.contains("super"); + boolean isCompound = flags.contains("compound"); + isCompactStorage = isSuper || isDense || !isCompound; + boolean isStaticCompact = !isSuper && !isDense && !isCompound; + if (isStaticCompact) { + RawColumn.pruneStaticCompactTableColumns(rawColumns); + } else if (isDense) { + RawColumn.pruneDenseTableColumnsV3(rawColumns); + } + } else { + boolean isDense = tableRow.getBoolean("is_dense"); + if (isDense) { + RawColumn.pruneDenseTableColumnsV2(rawColumns); + } + DataTypeClassNameCompositeParser.ParseResult comparator = + new DataTypeClassNameCompositeParser() + .parseWithComposite(tableRow.getString("comparator"), keyspaceId, userTypes, context); + isCompactStorage = isDense || !comparator.isComposite; + } + + Collections.sort(rawColumns); + ImmutableMap.Builder allColumnsBuilder = ImmutableMap.builder(); + ImmutableList.Builder partitionKeyBuilder = ImmutableList.builder(); + ImmutableMap.Builder clusteringColumnsBuilder = + ImmutableMap.builder(); + ImmutableMap.Builder indexesBuilder = ImmutableMap.builder(); + + for (RawColumn raw : rawColumns) { + DataType dataType = rows.dataTypeParser().parse(keyspaceId, raw.dataType, userTypes, context); + DseColumnMetadata column = + new DefaultDseColumnMetadata( + keyspaceId, tableId, raw.name, dataType, raw.kind.equals(RawColumn.KIND_STATIC)); + switch (raw.kind) { + case RawColumn.KIND_PARTITION_KEY: + partitionKeyBuilder.add(column); + break; + case RawColumn.KIND_CLUSTERING_COLUMN: + clusteringColumnsBuilder.put( + column, raw.reversed ? ClusteringOrder.DESC : ClusteringOrder.ASC); + break; + default: + // nothing to do + } + allColumnsBuilder.put(column.getName(), column); + + DseIndexMetadata index = buildLegacyIndex(raw, column); + if (index != null) { + indexesBuilder.put(index.getName(), index); + } + } + + Map options; + try { + options = parseOptions(tableRow); + } catch (Exception e) { + // Options change the most often, so be especially lenient if anything goes wrong. + Loggers.warnWithException( + LOG, + "[{}] Error while parsing options for {}.{}, getOptions() will be empty", + logPrefix, + keyspaceId, + tableId, + e); + options = Collections.emptyMap(); + } + + Collection indexRows = + rows.indexes().getOrDefault(keyspaceId, ImmutableMultimap.of()).get(tableId); + for (AdminRow indexRow : indexRows) { + DseIndexMetadata index = buildModernIndex(keyspaceId, tableId, indexRow); + indexesBuilder.put(index.getName(), index); + } + + return new DefaultDseTableMetadata( + keyspaceId, + tableId, + uuid, + isCompactStorage, + false, + partitionKeyBuilder.build(), + clusteringColumnsBuilder.build(), + allColumnsBuilder.build(), + options, + indexesBuilder.build(), + buildVertex(tableId, vertices), + buildEdge(tableId, edges, vertices)); + } + + DseTableMetadata parseVirtualTable(AdminRow tableRow, CqlIdentifier keyspaceId) { + + CqlIdentifier tableId = CqlIdentifier.fromInternal(tableRow.getString("table_name")); + + List rawColumns = + RawColumn.toRawColumns( + rows.virtualColumns().getOrDefault(keyspaceId, ImmutableMultimap.of()).get(tableId)); + if (rawColumns.isEmpty()) { + LOG.warn( + "[{}] Processing TABLE refresh for {}.{} but found no matching rows, skipping", + logPrefix, + keyspaceId, + tableId); + return null; + } + + Collections.sort(rawColumns); + ImmutableMap.Builder allColumnsBuilder = ImmutableMap.builder(); + ImmutableList.Builder partitionKeyBuilder = ImmutableList.builder(); + ImmutableMap.Builder clusteringColumnsBuilder = + ImmutableMap.builder(); + + for (RawColumn raw : rawColumns) { + DataType dataType = + rows.dataTypeParser().parse(keyspaceId, raw.dataType, Collections.emptyMap(), context); + DseColumnMetadata column = + new DefaultDseColumnMetadata( + keyspaceId, tableId, raw.name, dataType, raw.kind.equals(RawColumn.KIND_STATIC)); + switch (raw.kind) { + case RawColumn.KIND_PARTITION_KEY: + partitionKeyBuilder.add(column); + break; + case RawColumn.KIND_CLUSTERING_COLUMN: + clusteringColumnsBuilder.put( + column, raw.reversed ? ClusteringOrder.DESC : ClusteringOrder.ASC); + break; + default: + } + + allColumnsBuilder.put(column.getName(), column); + } + + return new DefaultDseTableMetadata( + keyspaceId, + tableId, + null, + false, + true, + partitionKeyBuilder.build(), + clusteringColumnsBuilder.build(), + allColumnsBuilder.build(), + Collections.emptyMap(), + Collections.emptyMap(), + null, + null); + } + + // In C*<=2.2, index information is stored alongside the column. + private DseIndexMetadata buildLegacyIndex(RawColumn raw, ColumnMetadata column) { + if (raw.indexName == null) { + return null; + } + return new DefaultDseIndexMetadata( + column.getKeyspace(), + column.getParent(), + CqlIdentifier.fromInternal(raw.indexName), + IndexKind.valueOf(raw.indexType), + buildLegacyIndexTarget(column, raw.indexOptions), + raw.indexOptions); + } + + private static String buildLegacyIndexTarget(ColumnMetadata column, Map options) { + String columnName = column.getName().asCql(true); + DataType columnType = column.getType(); + if (options.containsKey("index_keys")) { + return String.format("keys(%s)", columnName); + } + if (options.containsKey("index_keys_and_values")) { + return String.format("entries(%s)", columnName); + } + if ((columnType instanceof ListType && ((ListType) columnType).isFrozen()) + || (columnType instanceof SetType && ((SetType) columnType).isFrozen()) + || (columnType instanceof MapType && ((MapType) columnType).isFrozen())) { + return String.format("full(%s)", columnName); + } + // Note: the keyword 'values' is not accepted as a valid index target function until 3.0 + return columnName; + } + + // In C*>=3.0, index information is stored in a dedicated table: + // CREATE TABLE system_schema.indexes ( + // keyspace_name text, + // table_name text, + // index_name text, + // kind text, + // options frozen>, + // PRIMARY KEY (keyspace_name, table_name, index_name) + // ) WITH CLUSTERING ORDER BY (table_name ASC, index_name ASC) + private DseIndexMetadata buildModernIndex( + CqlIdentifier keyspaceId, CqlIdentifier tableId, AdminRow row) { + CqlIdentifier name = CqlIdentifier.fromInternal(row.getString("index_name")); + IndexKind kind = IndexKind.valueOf(row.getString("kind")); + Map options = row.getMapOfStringToString("options"); + String target = options.get("target"); + return new DefaultDseIndexMetadata(keyspaceId, tableId, name, kind, target, options); + } + + private DseVertexMetadata buildVertex( + CqlIdentifier tableId, Multimap keyspaceVertices) { + + if (keyspaceVertices == null) { + return null; + } + Collection tableVertices = keyspaceVertices.get(tableId); + if (tableVertices == null || tableVertices.isEmpty()) { + return null; + } + + AdminRow row = tableVertices.iterator().next(); + return new DefaultDseVertexMetadata(getLabel(row)); + } + + private DseEdgeMetadata buildEdge( + CqlIdentifier tableId, + Multimap keyspaceEdges, + Multimap keyspaceVertices) { + + if (keyspaceEdges == null) { + return null; + } + + Collection tableEdges = keyspaceEdges.get(tableId); + if (tableEdges == null || tableEdges.isEmpty()) { + return null; + } + + AdminRow row = tableEdges.iterator().next(); + + CqlIdentifier fromTable = CqlIdentifier.fromInternal(row.getString("from_table")); + + CqlIdentifier toTable = CqlIdentifier.fromInternal(row.getString("to_table")); + + return new DefaultDseEdgeMetadata( + getLabel(row), + fromTable, + findVertexLabel(fromTable, keyspaceVertices, "incoming"), + CqlIdentifiers.wrapInternal(row.getListOfString("from_partition_key_columns")), + CqlIdentifiers.wrapInternal(row.getListOfString("from_clustering_columns")), + toTable, + findVertexLabel(toTable, keyspaceVertices, "outgoing"), + CqlIdentifiers.wrapInternal(row.getListOfString("to_partition_key_columns")), + CqlIdentifiers.wrapInternal(row.getListOfString("to_clustering_columns"))); + } + + private CqlIdentifier getLabel(AdminRow row) { + String rawLabel = row.getString("label_name"); + return (rawLabel == null || rawLabel.isEmpty()) ? null : CqlIdentifier.fromInternal(rawLabel); + } + + // system_schema.edges only contains vertex table names. We also expose the labels in our metadata + // objects, so we need to look them up in system_schema.vertices. + private CqlIdentifier findVertexLabel( + CqlIdentifier table, + Multimap keyspaceVertices, + String directionForErrorMessage) { + Collection tableVertices = + (keyspaceVertices == null) ? null : keyspaceVertices.get(table); + if (tableVertices == null || tableVertices.isEmpty()) { + throw new IllegalArgumentException( + String.format( + "Missing vertex definition for %s table %s", + directionForErrorMessage, table.asCql(true))); + } + + AdminRow row = tableVertices.iterator().next(); + return getLabel(row); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/parsing/DseViewParser.java b/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/parsing/DseViewParser.java new file mode 100644 index 00000000000..07a1e2b5c39 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/metadata/schema/parsing/DseViewParser.java @@ -0,0 +1,157 @@ +/* + * 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 com.datastax.dse.driver.internal.core.metadata.schema.parsing; + +import com.datastax.dse.driver.api.core.metadata.schema.DseColumnMetadata; +import com.datastax.dse.driver.api.core.metadata.schema.DseViewMetadata; +import com.datastax.dse.driver.internal.core.metadata.schema.DefaultDseColumnMetadata; +import com.datastax.dse.driver.internal.core.metadata.schema.DefaultDseViewMetadata; +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.api.core.metadata.schema.ClusteringOrder; +import com.datastax.oss.driver.api.core.metadata.schema.ColumnMetadata; +import com.datastax.oss.driver.api.core.type.DataType; +import com.datastax.oss.driver.api.core.type.UserDefinedType; +import com.datastax.oss.driver.internal.core.adminrequest.AdminRow; +import com.datastax.oss.driver.internal.core.context.InternalDriverContext; +import com.datastax.oss.driver.internal.core.metadata.schema.parsing.RawColumn; +import com.datastax.oss.driver.internal.core.metadata.schema.parsing.RelationParser; +import com.datastax.oss.driver.internal.core.metadata.schema.queries.SchemaRows; +import com.datastax.oss.driver.internal.core.util.Loggers; +import com.datastax.oss.driver.shaded.guava.common.base.MoreObjects; +import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableList; +import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableMap; +import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableMultimap; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import net.jcip.annotations.ThreadSafe; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@ThreadSafe +public class DseViewParser extends RelationParser { + + private static final Logger LOG = LoggerFactory.getLogger(DseViewParser.class); + + public DseViewParser(SchemaRows rows, InternalDriverContext context) { + super(rows, context); + } + + public DseViewMetadata parseView( + AdminRow viewRow, CqlIdentifier keyspaceId, Map userTypes) { + // Cassandra 3.0 (no views in earlier versions): + // CREATE TABLE system_schema.views ( + // keyspace_name text, + // view_name text, + // base_table_id uuid, + // base_table_name text, + // bloom_filter_fp_chance double, + // caching frozen>, + // cdc boolean, + // comment text, + // compaction frozen>, + // compression frozen>, + // crc_check_chance double, + // dclocal_read_repair_chance double, + // default_time_to_live int, + // extensions frozen>, + // gc_grace_seconds int, + // id uuid, + // include_all_columns boolean, + // max_index_interval int, + // memtable_flush_period_in_ms int, + // min_index_interval int, + // read_repair_chance double, + // speculative_retry text, + // where_clause text, + // PRIMARY KEY (keyspace_name, view_name) + // ) WITH CLUSTERING ORDER BY (view_name ASC) + CqlIdentifier viewId = CqlIdentifier.fromInternal(viewRow.getString("view_name")); + + UUID uuid = viewRow.getUuid("id"); + CqlIdentifier baseTableId = CqlIdentifier.fromInternal(viewRow.getString("base_table_name")); + boolean includesAllColumns = + MoreObjects.firstNonNull(viewRow.getBoolean("include_all_columns"), false); + String whereClause = viewRow.getString("where_clause"); + + List rawColumns = + RawColumn.toRawColumns( + rows.columns().getOrDefault(keyspaceId, ImmutableMultimap.of()).get(viewId)); + if (rawColumns.isEmpty()) { + LOG.warn( + "[{}] Processing VIEW refresh for {}.{} but found no matching rows, skipping", + logPrefix, + keyspaceId, + viewId); + return null; + } + + Collections.sort(rawColumns); + ImmutableMap.Builder allColumnsBuilder = ImmutableMap.builder(); + ImmutableList.Builder partitionKeyBuilder = ImmutableList.builder(); + ImmutableMap.Builder clusteringColumnsBuilder = + ImmutableMap.builder(); + + for (RawColumn raw : rawColumns) { + DataType dataType = rows.dataTypeParser().parse(keyspaceId, raw.dataType, userTypes, context); + DseColumnMetadata column = + new DefaultDseColumnMetadata( + keyspaceId, viewId, raw.name, dataType, raw.kind.equals(RawColumn.KIND_STATIC)); + switch (raw.kind) { + case RawColumn.KIND_PARTITION_KEY: + partitionKeyBuilder.add(column); + break; + case RawColumn.KIND_CLUSTERING_COLUMN: + clusteringColumnsBuilder.put( + column, raw.reversed ? ClusteringOrder.DESC : ClusteringOrder.ASC); + break; + default: + // nothing to do + } + allColumnsBuilder.put(column.getName(), column); + } + + Map options; + try { + options = parseOptions(viewRow); + } catch (Exception e) { + // Options change the most often, so be especially lenient if anything goes wrong. + Loggers.warnWithException( + LOG, + "[{}] Error while parsing options for {}.{}, getOptions() will be empty", + logPrefix, + keyspaceId, + viewId, + e); + options = Collections.emptyMap(); + } + + return new DefaultDseViewMetadata( + keyspaceId, + viewId, + baseTableId, + includesAllColumns, + whereClause, + uuid, + partitionKeyBuilder.build(), + clusteringColumnsBuilder.build(), + allColumnsBuilder.build(), + options); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/protocol/TinkerpopBufferPrimitiveCodec.java b/core/src/main/java/com/datastax/dse/driver/internal/core/protocol/TinkerpopBufferPrimitiveCodec.java new file mode 100644 index 00000000000..13238519e06 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/protocol/TinkerpopBufferPrimitiveCodec.java @@ -0,0 +1,292 @@ +/* + * 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 com.datastax.dse.driver.internal.core.protocol; + +import com.datastax.dse.driver.internal.core.graph.binary.buffer.DseNettyBufferFactory; +import com.datastax.oss.driver.internal.core.protocol.ByteBufPrimitiveCodec; +import com.datastax.oss.driver.shaded.guava.common.base.Charsets; +import com.datastax.oss.protocol.internal.PrimitiveCodec; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.zip.CRC32; +import org.apache.tinkerpop.gremlin.structure.io.Buffer; + +/** + * Minimal implementation of {@link PrimitiveCodec} for Tinkerpop {@link Buffer} instances. + * + *

This approach represents a temporary design compromise. PrimitiveCodec is primarily used for + * handling data directly from Netty, a task satisfied by {@link ByteBufPrimitiveCodec}. But + * PrimitiveCodec is also used to implement graph serialization for some of the "dynamic" types + * (notably UDTs and tuples). Since we're converting graph serialization to use the new Tinkerpop + * Buffer API we need just enough of a PrimitiveCodec impl to satisfy the needs of graph + * serialization... and nothing more. + * + *

A more explicit approach would be to change graph serialization to use a different interface, + * some kind of subset of PrimitiveCodec.... and then make PrimitiveCodec extend this interface. + * This is left as future work for now since it involves changes to the native-protocol lib(s). + */ +public class TinkerpopBufferPrimitiveCodec implements PrimitiveCodec { + + private final DseNettyBufferFactory factory; + + public TinkerpopBufferPrimitiveCodec(DseNettyBufferFactory factory) { + this.factory = factory; + } + + @Override + public Buffer allocate(int size) { + // Note: we use io() here to match up to what ByteBufPrimitiveCodec does, but be warned that + // ByteBufs created in this way don't support the array() method used elsewhere in this codec + // (readString() specifically). As such usage of this method to create Buffer instances is + // discouraged; we have a factory for that. + return this.factory.io(size, size); + } + + @Override + public void release(Buffer toRelease) { + toRelease.release(); + } + + @Override + public int sizeOf(Buffer toMeasure) { + return toMeasure.readableBytes(); + } + + // TODO + @Override + public Buffer concat(Buffer left, Buffer right) { + boolean leftReadable = left.readableBytes() > 0; + boolean rightReadable = right.readableBytes() > 0; + if (!(leftReadable || rightReadable)) { + return factory.heap(); + } + if (!leftReadable) { + return right; + } + if (!rightReadable) { + return left; + } + Buffer rv = factory.composite(left, right); + // c.readerIndex() is 0, which is the first readable byte in left + rv.writerIndex( + left.writerIndex() - left.readerIndex() + right.writerIndex() - right.readerIndex()); + return rv; + } + + @Override + public void markReaderIndex(Buffer source) { + throw new UnsupportedOperationException(); + } + + @Override + public void resetReaderIndex(Buffer source) { + throw new UnsupportedOperationException(); + } + + @Override + public byte readByte(Buffer source) { + return source.readByte(); + } + + @Override + public int readInt(Buffer source) { + return source.readInt(); + } + + @Override + public int readInt(Buffer source, int offset) { + throw new UnsupportedOperationException(); + } + + @Override + public InetAddress readInetAddr(Buffer source) { + int length = readByte(source) & 0xFF; + byte[] bytes = new byte[length]; + source.readBytes(bytes); + return newInetAddress(bytes); + } + + @Override + public long readLong(Buffer source) { + return source.readLong(); + } + + @Override + public int readUnsignedShort(Buffer source) { + return source.readShort() & 0xFFFF; + } + + @Override + public ByteBuffer readBytes(Buffer source) { + int length = readInt(source); + if (length < 0) return null; + return source.nioBuffer(source.readerIndex(), length); + } + + @Override + public byte[] readShortBytes(Buffer source) { + try { + int length = readUnsignedShort(source); + byte[] bytes = new byte[length]; + source.readBytes(bytes); + return bytes; + } catch (IndexOutOfBoundsException e) { + throw new IllegalArgumentException( + "Not enough bytes to read a byte array preceded by its 2 bytes length"); + } + } + + // Copy of PrimitiveCodec impl + @Override + public String readString(Buffer source) { + int length = readUnsignedShort(source); + return readString(source, length); + } + + @Override + public String readLongString(Buffer source) { + int length = readInt(source); + return readString(source, length); + } + + @Override + public Buffer readRetainedSlice(Buffer source, int sliceLength) { + throw new UnsupportedOperationException(); + } + + @Override + public void updateCrc(Buffer source, CRC32 crc) { + throw new UnsupportedOperationException(); + } + + @Override + public void writeByte(byte b, Buffer dest) { + dest.writeByte(b); + } + + @Override + public void writeInt(int i, Buffer dest) { + dest.writeInt(i); + } + + @Override + public void writeInetAddr(InetAddress address, Buffer dest) { + byte[] bytes = address.getAddress(); + writeByte((byte) bytes.length, dest); + dest.writeBytes(bytes); + } + + @Override + public void writeLong(long l, Buffer dest) { + dest.writeLong(l); + } + + @Override + public void writeUnsignedShort(int i, Buffer dest) { + dest.writeShort(i); + } + + // Copy of PrimitiveCodec impl + @Override + public void writeString(String s, Buffer dest) { + + byte[] bytes = s.getBytes(Charsets.UTF_8); + writeUnsignedShort(bytes.length, dest); + dest.writeBytes(bytes); + } + + @Override + public void writeLongString(String s, Buffer dest) { + byte[] bytes = s.getBytes(Charsets.UTF_8); + writeInt(bytes.length, dest); + dest.writeBytes(bytes); + } + + @Override + public void writeBytes(ByteBuffer bytes, Buffer dest) { + if (bytes == null) { + writeInt(-1, dest); + } else { + writeInt(bytes.remaining(), dest); + dest.writeBytes(bytes.duplicate()); + } + } + + @Override + public void writeBytes(byte[] bytes, Buffer dest) { + if (bytes == null) { + writeInt(-1, dest); + } else { + writeInt(bytes.length, dest); + dest.writeBytes(bytes); + } + } + + @Override + public void writeShortBytes(byte[] bytes, Buffer dest) { + writeUnsignedShort(bytes.length, dest); + dest.writeBytes(bytes); + } + + // Based on PrimitiveCodec impl, although that method leverages some + // Netty built-ins which we have to do manually here + private static String readString(Buffer buff, int length) { + try { + + // Basically what io.netty.buffer.ByteBufUtil.decodeString() does minus some extra + // ByteBuf-specific ops + int offset; + byte[] bytes; + ByteBuffer byteBuff = buff.nioBuffer(); + if (byteBuff.hasArray()) { + + bytes = byteBuff.array(); + offset = byteBuff.arrayOffset(); + } else { + + bytes = new byte[length]; + byteBuff.get(bytes, 0, length); + offset = 0; + } + + String str = new String(bytes, offset, length, Charsets.UTF_8); + + // Ops against the NIO buffers don't impact the read/write indexes for he Buffer + // itself so we have to do that manually + buff.readerIndex(buff.readerIndex() + length); + return str; + } catch (IndexOutOfBoundsException e) { + throw new IllegalArgumentException( + "Not enough bytes to read an UTF-8 serialized string of size " + length, e); + } + } + + // TODO: Code below copied directly from ByteBufPrimitiveCodec, probably want to consolidate this + // somewhere + private static InetAddress newInetAddress(byte[] bytes) { + try { + return InetAddress.getByAddress(bytes); + } catch (UnknownHostException e) { + // Per the Javadoc, the only way this can happen is if the length is illegal + throw new IllegalArgumentException( + String.format("Invalid address length: %d (%s)", bytes.length, Arrays.toString(bytes))); + } + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/search/DateRangeUtil.java b/core/src/main/java/com/datastax/dse/driver/internal/core/search/DateRangeUtil.java new file mode 100644 index 00000000000..15e278260c5 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/search/DateRangeUtil.java @@ -0,0 +1,230 @@ +/* + * 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 com.datastax.dse.driver.internal.core.search; + +import com.datastax.dse.driver.api.core.data.time.DateRangePrecision; +import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableMap; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.text.ParseException; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoField; +import java.time.temporal.ChronoUnit; +import java.time.temporal.TemporalAdjusters; +import java.util.Calendar; +import java.util.Locale; +import java.util.Map; +import java.util.TimeZone; + +public class DateRangeUtil { + + /** Sets all the fields smaller than the given unit to their lowest possible value. */ + @NonNull + public static ZonedDateTime roundDown(@NonNull ZonedDateTime date, @NonNull ChronoUnit unit) { + switch (unit) { + case YEARS: + return date.with(TemporalAdjusters.firstDayOfYear()).truncatedTo(ChronoUnit.DAYS); + case MONTHS: + return date.with(TemporalAdjusters.firstDayOfMonth()).truncatedTo(ChronoUnit.DAYS); + case DAYS: + case HOURS: + case MINUTES: + case SECONDS: + case MILLIS: + return date.truncatedTo(unit); + default: + throw new IllegalArgumentException("Unsupported unit for rounding: " + unit); + } + } + + /** Sets all the fields smaller than the given unit to their highest possible value. */ + @NonNull + public static ZonedDateTime roundUp(@NonNull ZonedDateTime date, @NonNull ChronoUnit unit) { + return roundDown(date, unit) + .plus(1, unit) + // Even though ZDT has nanosecond-precision, DSE only rounds to millisecond precision so be + // consistent with that + .minus(1, ChronoUnit.MILLIS); + } + + /** + * Parses the given string as a date in a range bound. + * + *

This method deliberately uses legacy time APIs, in order to be as close as possible to the + * server-side parsing logic. We want the client to behave exactly like the server, i.e. parsing a + * date locally and inlining it in a CQL query should always yield the same result as binding the + * date as a value. + */ + public static Calendar parseCalendar(String source) throws ParseException { + // The contents of this method are based on Lucene's DateRangePrefixTree#parseCalendar, released + // under the Apache License, Version 2.0. + // Following is the original notice from that file: + + // 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. + + if (source == null || source.isEmpty()) { + throw new IllegalArgumentException("Can't parse a null or blank string"); + } + + Calendar calendar = newCalendar(); + if (source.equals("*")) { + return calendar; + } + int offset = 0; // a pointer + try { + // year & era: + int lastOffset = + (source.charAt(source.length() - 1) == 'Z') ? source.length() - 1 : source.length(); + int hyphenIdx = source.indexOf('-', 1); // look past possible leading hyphen + if (hyphenIdx < 0) { + hyphenIdx = lastOffset; + } + int year = Integer.parseInt(source.substring(offset, hyphenIdx)); + calendar.set(Calendar.ERA, year <= 0 ? 0 : 1); + calendar.set(Calendar.YEAR, year <= 0 ? -1 * year + 1 : year); + offset = hyphenIdx + 1; + if (lastOffset < offset) { + return calendar; + } + + // NOTE: We aren't validating separator chars, and we unintentionally accept leading +/-. + // The str.substring()'s hopefully get optimized to be stack-allocated. + + // month: + calendar.set( + Calendar.MONTH, + Integer.parseInt(source.substring(offset, offset + 2)) - 1); // starts at 0 + offset += 3; + if (lastOffset < offset) { + return calendar; + } + // day: + calendar.set(Calendar.DAY_OF_MONTH, Integer.parseInt(source.substring(offset, offset + 2))); + offset += 3; + if (lastOffset < offset) { + return calendar; + } + // hour: + calendar.set(Calendar.HOUR_OF_DAY, Integer.parseInt(source.substring(offset, offset + 2))); + offset += 3; + if (lastOffset < offset) { + return calendar; + } + // minute: + calendar.set(Calendar.MINUTE, Integer.parseInt(source.substring(offset, offset + 2))); + offset += 3; + if (lastOffset < offset) { + return calendar; + } + // second: + calendar.set(Calendar.SECOND, Integer.parseInt(source.substring(offset, offset + 2))); + offset += 3; + if (lastOffset < offset) { + return calendar; + } + // ms: + calendar.set(Calendar.MILLISECOND, Integer.parseInt(source.substring(offset, offset + 3))); + offset += 3; // last one, move to next char + if (lastOffset == offset) { + return calendar; + } + } catch (Exception e) { + ParseException pe = new ParseException("Improperly formatted date: " + source, offset); + pe.initCause(e); + throw pe; + } + throw new ParseException("Improperly formatted date: " + source, offset); + } + + private static Calendar newCalendar() { + Calendar calendar = Calendar.getInstance(UTC, Locale.ROOT); + calendar.clear(); + return calendar; + } + + private static final TimeZone UTC = TimeZone.getTimeZone("UTC"); + + /** + * Returns the precision of a calendar obtained through {@link #parseCalendar(String)}, or {@code + * null} if no field was set. + */ + @Nullable + public static DateRangePrecision getPrecision(Calendar calendar) { + DateRangePrecision lastPrecision = null; + for (Map.Entry entry : FIELD_BY_PRECISION.entrySet()) { + DateRangePrecision precision = entry.getKey(); + int field = entry.getValue(); + if (calendar.isSet(field)) { + lastPrecision = precision; + } else { + break; + } + } + return lastPrecision; + } + + // Note: this could be a field on DateRangePrecision, but it's only used within this class so it's + // better not to expose it. + private static final ImmutableMap FIELD_BY_PRECISION = + ImmutableMap.builder() + .put(DateRangePrecision.YEAR, Calendar.YEAR) + .put(DateRangePrecision.MONTH, Calendar.MONTH) + .put(DateRangePrecision.DAY, Calendar.DAY_OF_MONTH) + .put(DateRangePrecision.HOUR, Calendar.HOUR_OF_DAY) + .put(DateRangePrecision.MINUTE, Calendar.MINUTE) + .put(DateRangePrecision.SECOND, Calendar.SECOND) + .put(DateRangePrecision.MILLISECOND, Calendar.MILLISECOND) + .build(); + + public static ZonedDateTime toZonedDateTime(Calendar calendar) { + int year = calendar.get(Calendar.YEAR); + if (calendar.get(Calendar.ERA) == 0) { + // BC era; 1 BC == 0 AD, 0 BD == -1 AD, etc + year -= 1; + if (year > 0) { + year = -year; + } + } + LocalDateTime localDateTime = + LocalDateTime.of( + year, + calendar.get(Calendar.MONTH) + 1, + calendar.get(Calendar.DAY_OF_MONTH), + calendar.get(Calendar.HOUR_OF_DAY), + calendar.get(Calendar.MINUTE), + calendar.get(Calendar.SECOND)); + localDateTime = + localDateTime.with(ChronoField.MILLI_OF_SECOND, calendar.get(Calendar.MILLISECOND)); + return ZonedDateTime.of(localDateTime, ZoneOffset.UTC); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/session/DefaultDseSession.java b/core/src/main/java/com/datastax/dse/driver/internal/core/session/DefaultDseSession.java new file mode 100644 index 00000000000..183f385aa4a --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/session/DefaultDseSession.java @@ -0,0 +1,38 @@ +/* + * 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 com.datastax.dse.driver.internal.core.session; + +import com.datastax.oss.driver.api.core.CqlSession; +import com.datastax.oss.driver.api.core.session.Session; +import com.datastax.oss.driver.internal.core.session.DefaultSession; +import com.datastax.oss.driver.internal.core.session.SessionWrapper; +import net.jcip.annotations.ThreadSafe; + +/** + * @deprecated DSE functionality is now exposed directly on {@link CqlSession}. This class is + * preserved for backward compatibility, but {@link DefaultSession} should be used instead. + */ +@ThreadSafe +@Deprecated +public class DefaultDseSession extends SessionWrapper + implements com.datastax.dse.driver.api.core.DseSession { + + public DefaultDseSession(Session delegate) { + super(delegate); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/type/codec/DseTypeCodecsRegistrar.java b/core/src/main/java/com/datastax/dse/driver/internal/core/type/codec/DseTypeCodecsRegistrar.java new file mode 100644 index 00000000000..55da2a9475f --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/type/codec/DseTypeCodecsRegistrar.java @@ -0,0 +1,40 @@ +/* + * 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 com.datastax.dse.driver.internal.core.type.codec; + +import static com.datastax.oss.driver.internal.core.util.Dependency.ESRI; + +import com.datastax.dse.driver.api.core.type.codec.DseTypeCodecs; +import com.datastax.oss.driver.api.core.type.codec.registry.MutableCodecRegistry; +import com.datastax.oss.driver.internal.core.util.DefaultDependencyChecker; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DseTypeCodecsRegistrar { + + private static final Logger LOG = LoggerFactory.getLogger(DseTypeCodecsRegistrar.class); + + public static void registerDseCodecs(MutableCodecRegistry registry) { + registry.register(DseTypeCodecs.DATE_RANGE); + if (DefaultDependencyChecker.isPresent(ESRI)) { + registry.register(DseTypeCodecs.LINE_STRING, DseTypeCodecs.POINT, DseTypeCodecs.POLYGON); + } else { + LOG.debug("ESRI was not found on the classpath: geo codecs will not be available"); + } + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/type/codec/DseTypeCodecsRegistrarSubstitutions.java b/core/src/main/java/com/datastax/dse/driver/internal/core/type/codec/DseTypeCodecsRegistrarSubstitutions.java new file mode 100644 index 00000000000..afd8d6cf9f6 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/type/codec/DseTypeCodecsRegistrarSubstitutions.java @@ -0,0 +1,47 @@ +/* + * 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 com.datastax.dse.driver.internal.core.type.codec; + +import static com.datastax.oss.driver.internal.core.util.Dependency.ESRI; + +import com.datastax.dse.driver.api.core.type.codec.DseTypeCodecs; +import com.datastax.oss.driver.api.core.type.codec.registry.MutableCodecRegistry; +import com.datastax.oss.driver.internal.core.util.GraalDependencyChecker; +import com.oracle.svm.core.annotate.Substitute; +import com.oracle.svm.core.annotate.TargetClass; +import java.util.function.BooleanSupplier; + +@SuppressWarnings("unused") +public class DseTypeCodecsRegistrarSubstitutions { + + @TargetClass(value = DseTypeCodecsRegistrar.class, onlyWith = EsriMissing.class) + public static final class DseTypeCodecsRegistrarEsriMissing { + + @Substitute + public static void registerDseCodecs(MutableCodecRegistry registry) { + registry.register(DseTypeCodecs.DATE_RANGE); + } + } + + public static class EsriMissing implements BooleanSupplier { + @Override + public boolean getAsBoolean() { + return !GraalDependencyChecker.isPresent(ESRI); + } + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/type/codec/geometry/GeometryCodec.java b/core/src/main/java/com/datastax/dse/driver/internal/core/type/codec/geometry/GeometryCodec.java new file mode 100644 index 00000000000..f6309bc1860 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/type/codec/geometry/GeometryCodec.java @@ -0,0 +1,113 @@ +/* + * 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 com.datastax.dse.driver.internal.core.type.codec.geometry; + +import static com.datastax.oss.driver.internal.core.util.Strings.isQuoted; + +import com.datastax.dse.driver.api.core.data.geometry.Geometry; +import com.datastax.oss.driver.api.core.ProtocolVersion; +import com.datastax.oss.driver.api.core.type.codec.TypeCodec; +import com.datastax.oss.driver.internal.core.util.Strings; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.nio.ByteBuffer; +import net.jcip.annotations.ThreadSafe; + +/** Base class for geospatial type codecs. */ +@ThreadSafe +public abstract class GeometryCodec implements TypeCodec { + + @Nullable + @Override + public T decode(@Nullable ByteBuffer bb, @NonNull ProtocolVersion protocolVersion) { + return bb == null || bb.remaining() == 0 ? null : fromWellKnownBinary(bb.slice()); + } + + @Nullable + @Override + public ByteBuffer encode(@Nullable T geometry, @NonNull ProtocolVersion protocolVersion) { + return geometry == null ? null : toWellKnownBinary(geometry); + } + + @Nullable + @Override + public T parse(@Nullable String s) { + if (s == null) { + return null; + } + s = s.trim(); + if (s.isEmpty() || s.equalsIgnoreCase("NULL")) { + return null; + } + if (!isQuoted(s)) { + throw new IllegalArgumentException("Geometry values must be enclosed by single quotes"); + } + return fromWellKnownText(Strings.unquote(s)); + } + + @NonNull + @Override + public String format(@Nullable T geometry) throws IllegalArgumentException { + return geometry == null ? "NULL" : Strings.quote(toWellKnownText(geometry)); + } + + /** + * Creates an instance of this codec's geospatial type from its Well-known Text (WKT) representation. + * + * @param source the Well-known Text representation to parse. Cannot be null. + * @return A new instance of this codec's geospatial type. + * @throws IllegalArgumentException if the string does not contain a valid Well-known Text + * representation. + */ + @NonNull + protected abstract T fromWellKnownText(@NonNull String source); + + /** + * Creates an instance of a geospatial type from its Well-known Binary + * (WKB) representation. + * + * @param bb the Well-known Binary representation to parse. Cannot be null. + * @return A new instance of this codec's geospatial type. + * @throws IllegalArgumentException if the given {@link ByteBuffer} does not contain a valid + * Well-known Binary representation. + */ + @NonNull + protected abstract T fromWellKnownBinary(@NonNull ByteBuffer bb); + + /** + * Returns a Well-known Text (WKT) + * representation of the given geospatial object. + * + * @param geometry the geospatial object to convert. Cannot be null. + * @return A Well-known Text representation of the given object. + */ + @NonNull + protected abstract String toWellKnownText(@NonNull T geometry); + + /** + * Returns a Well-known + * Binary (WKB) representation of the given geospatial object. + * + * @param geometry the geospatial object to convert. Cannot be null. + * @return A Well-known Binary representation of the given object. + */ + @NonNull + protected abstract ByteBuffer toWellKnownBinary(@NonNull T geometry); +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/type/codec/geometry/LineStringCodec.java b/core/src/main/java/com/datastax/dse/driver/internal/core/type/codec/geometry/LineStringCodec.java new file mode 100644 index 00000000000..bbec99a4103 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/type/codec/geometry/LineStringCodec.java @@ -0,0 +1,87 @@ +/* + * 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 com.datastax.dse.driver.internal.core.type.codec.geometry; + +import com.datastax.dse.driver.api.core.data.geometry.LineString; +import com.datastax.dse.driver.api.core.type.DseDataTypes; +import com.datastax.dse.driver.internal.core.data.geometry.DefaultGeometry; +import com.datastax.dse.driver.internal.core.data.geometry.DefaultLineString; +import com.datastax.oss.driver.api.core.type.DataType; +import com.datastax.oss.driver.api.core.type.reflect.GenericType; +import com.esri.core.geometry.ogc.OGCLineString; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.nio.ByteBuffer; +import net.jcip.annotations.ThreadSafe; + +/** + * A custom type codec to use {@link LineString} instances in driver. + * + *

If you use {@link com.datastax.dse.driver.api.core.DseSessionBuilder} to build your cluster, + * it will automatically register this codec. + */ +@ThreadSafe +public class LineStringCodec extends GeometryCodec { + + private static final GenericType JAVA_TYPE = GenericType.of(LineString.class); + + @NonNull + @Override + public GenericType getJavaType() { + return JAVA_TYPE; + } + + @NonNull + @Override + protected LineString fromWellKnownText(@NonNull String source) { + return new DefaultLineString(DefaultGeometry.fromOgcWellKnownText(source, OGCLineString.class)); + } + + @Override + public boolean accepts(@NonNull Class javaClass) { + return javaClass == LineString.class; + } + + @Override + public boolean accepts(@NonNull Object value) { + return value instanceof LineString; + } + + @NonNull + @Override + protected LineString fromWellKnownBinary(@NonNull ByteBuffer bb) { + return new DefaultLineString(DefaultGeometry.fromOgcWellKnownBinary(bb, OGCLineString.class)); + } + + @NonNull + @Override + protected String toWellKnownText(@NonNull LineString geometry) { + return geometry.asWellKnownText(); + } + + @NonNull + @Override + protected ByteBuffer toWellKnownBinary(@NonNull LineString geometry) { + return geometry.asWellKnownBinary(); + } + + @NonNull + @Override + public DataType getCqlType() { + return DseDataTypes.LINE_STRING; + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/type/codec/geometry/PointCodec.java b/core/src/main/java/com/datastax/dse/driver/internal/core/type/codec/geometry/PointCodec.java new file mode 100644 index 00000000000..5ebae64cbab --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/type/codec/geometry/PointCodec.java @@ -0,0 +1,87 @@ +/* + * 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 com.datastax.dse.driver.internal.core.type.codec.geometry; + +import com.datastax.dse.driver.api.core.data.geometry.Point; +import com.datastax.dse.driver.api.core.type.DseDataTypes; +import com.datastax.dse.driver.internal.core.data.geometry.DefaultGeometry; +import com.datastax.dse.driver.internal.core.data.geometry.DefaultPoint; +import com.datastax.oss.driver.api.core.type.DataType; +import com.datastax.oss.driver.api.core.type.reflect.GenericType; +import com.esri.core.geometry.ogc.OGCPoint; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.nio.ByteBuffer; +import net.jcip.annotations.ThreadSafe; + +/** + * A custom type codec to use {@link Point} instances in the driver. + * + *

If you use {@link com.datastax.dse.driver.api.core.DseSessionBuilder} to build your cluster, + * it will automatically register this codec. + */ +@ThreadSafe +public class PointCodec extends GeometryCodec { + + private static final GenericType JAVA_TYPE = GenericType.of(Point.class); + + @NonNull + @Override + public GenericType getJavaType() { + return JAVA_TYPE; + } + + @NonNull + @Override + public DataType getCqlType() { + return DseDataTypes.POINT; + } + + @Override + public boolean accepts(@NonNull Class javaClass) { + return javaClass == Point.class; + } + + @Override + public boolean accepts(@NonNull Object value) { + return value instanceof Point; + } + + @NonNull + @Override + protected String toWellKnownText(@NonNull Point geometry) { + return geometry.asWellKnownText(); + } + + @NonNull + @Override + protected ByteBuffer toWellKnownBinary(@NonNull Point geometry) { + return geometry.asWellKnownBinary(); + } + + @NonNull + @Override + protected Point fromWellKnownText(@NonNull String source) { + return new DefaultPoint(DefaultGeometry.fromOgcWellKnownText(source, OGCPoint.class)); + } + + @NonNull + @Override + protected Point fromWellKnownBinary(@NonNull ByteBuffer source) { + return new DefaultPoint(DefaultGeometry.fromOgcWellKnownBinary(source, OGCPoint.class)); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/type/codec/geometry/PolygonCodec.java b/core/src/main/java/com/datastax/dse/driver/internal/core/type/codec/geometry/PolygonCodec.java new file mode 100644 index 00000000000..00a070a4b4a --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/type/codec/geometry/PolygonCodec.java @@ -0,0 +1,87 @@ +/* + * 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 com.datastax.dse.driver.internal.core.type.codec.geometry; + +import com.datastax.dse.driver.api.core.data.geometry.Polygon; +import com.datastax.dse.driver.api.core.type.DseDataTypes; +import com.datastax.dse.driver.internal.core.data.geometry.DefaultGeometry; +import com.datastax.dse.driver.internal.core.data.geometry.DefaultPolygon; +import com.datastax.oss.driver.api.core.type.DataType; +import com.datastax.oss.driver.api.core.type.reflect.GenericType; +import com.esri.core.geometry.ogc.OGCPolygon; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.nio.ByteBuffer; +import net.jcip.annotations.ThreadSafe; + +/** + * A custom type codec to use {@link Polygon} instances in the driver. + * + *

If you use {@link com.datastax.dse.driver.api.core.DseSessionBuilder} to build your cluster, + * it will automatically register this codec. + */ +@ThreadSafe +public class PolygonCodec extends GeometryCodec { + + private static final GenericType JAVA_TYPE = GenericType.of(Polygon.class); + + @NonNull + @Override + public GenericType getJavaType() { + return JAVA_TYPE; + } + + @NonNull + @Override + public DataType getCqlType() { + return DseDataTypes.POLYGON; + } + + @Override + public boolean accepts(@NonNull Class javaClass) { + return javaClass == Polygon.class; + } + + @Override + public boolean accepts(@NonNull Object value) { + return value instanceof Polygon; + } + + @NonNull + @Override + protected Polygon fromWellKnownText(@NonNull String source) { + return new DefaultPolygon(DefaultGeometry.fromOgcWellKnownText(source, OGCPolygon.class)); + } + + @NonNull + @Override + protected Polygon fromWellKnownBinary(@NonNull ByteBuffer bb) { + return new DefaultPolygon(DefaultGeometry.fromOgcWellKnownBinary(bb, OGCPolygon.class)); + } + + @NonNull + @Override + protected String toWellKnownText(@NonNull Polygon geometry) { + return geometry.asWellKnownText(); + } + + @NonNull + @Override + protected ByteBuffer toWellKnownBinary(@NonNull Polygon geometry) { + return geometry.asWellKnownBinary(); + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/type/codec/time/DateRangeCodec.java b/core/src/main/java/com/datastax/dse/driver/internal/core/type/codec/time/DateRangeCodec.java new file mode 100644 index 00000000000..e8a23e88848 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/type/codec/time/DateRangeCodec.java @@ -0,0 +1,188 @@ +/* + * 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 com.datastax.dse.driver.internal.core.type.codec.time; + +import com.datastax.dse.driver.api.core.data.time.DateRange; +import com.datastax.dse.driver.api.core.data.time.DateRangeBound; +import com.datastax.dse.driver.api.core.data.time.DateRangePrecision; +import com.datastax.dse.driver.api.core.type.DseDataTypes; +import com.datastax.oss.driver.api.core.ProtocolVersion; +import com.datastax.oss.driver.api.core.type.DataType; +import com.datastax.oss.driver.api.core.type.codec.TypeCodec; +import com.datastax.oss.driver.api.core.type.reflect.GenericType; +import com.datastax.oss.driver.internal.core.util.Strings; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.nio.ByteBuffer; +import java.text.ParseException; +import java.time.Instant; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.util.Optional; + +public class DateRangeCodec implements TypeCodec { + + private static final GenericType JAVA_TYPE = GenericType.of(DateRange.class); + private static final DataType CQL_TYPE = DseDataTypes.DATE_RANGE; + + // e.g. [2001-01-01] + private static final byte DATE_RANGE_TYPE_SINGLE_DATE = 0x00; + // e.g. [2001-01-01 TO 2001-01-31] + private static final byte DATE_RANGE_TYPE_CLOSED_RANGE = 0x01; + // e.g. [2001-01-01 TO *] + private static final byte DATE_RANGE_TYPE_OPEN_RANGE_HIGH = 0x02; + // e.g. [* TO 2001-01-01] + private static final byte DATE_RANGE_TYPE_OPEN_RANGE_LOW = 0x03; + // [* TO *] + private static final byte DATE_RANGE_TYPE_BOTH_OPEN_RANGE = 0x04; + // * + private static final byte DATE_RANGE_TYPE_SINGLE_DATE_OPEN = 0x05; + + @NonNull + @Override + public GenericType getJavaType() { + return JAVA_TYPE; + } + + @NonNull + @Override + public DataType getCqlType() { + return CQL_TYPE; + } + + @Override + public boolean accepts(@NonNull Class javaClass) { + return javaClass == DateRange.class; + } + + @Nullable + @Override + public ByteBuffer encode( + @Nullable DateRange dateRange, @NonNull ProtocolVersion protocolVersion) { + if (dateRange == null) { + return null; + } + byte rangeType = encodeType(dateRange); + int bufferSize = 1; + DateRangeBound lowerBound = dateRange.getLowerBound(); + Optional maybeUpperBound = dateRange.getUpperBound(); + bufferSize += lowerBound.isUnbounded() ? 0 : 9; + bufferSize += maybeUpperBound.map(upperBound -> upperBound.isUnbounded() ? 0 : 9).orElse(0); + ByteBuffer buffer = ByteBuffer.allocate(bufferSize); + buffer.put(rangeType); + if (!lowerBound.isUnbounded()) { + put(buffer, lowerBound); + } + maybeUpperBound.ifPresent( + upperBound -> { + if (!upperBound.isUnbounded()) { + put(buffer, upperBound); + } + }); + return (ByteBuffer) buffer.flip(); + } + + private static byte encodeType(DateRange dateRange) { + if (dateRange.isSingleBounded()) { + return dateRange.getLowerBound().isUnbounded() + ? DATE_RANGE_TYPE_SINGLE_DATE_OPEN + : DATE_RANGE_TYPE_SINGLE_DATE; + } else { + DateRangeBound upperBound = + dateRange + .getUpperBound() + .orElseThrow( + () -> + new IllegalStateException("Upper bound should be set if !isSingleBounded()")); + if (dateRange.getLowerBound().isUnbounded()) { + return upperBound.isUnbounded() + ? DATE_RANGE_TYPE_BOTH_OPEN_RANGE + : DATE_RANGE_TYPE_OPEN_RANGE_LOW; + } else { + return upperBound.isUnbounded() + ? DATE_RANGE_TYPE_OPEN_RANGE_HIGH + : DATE_RANGE_TYPE_CLOSED_RANGE; + } + } + } + + private static void put(ByteBuffer buffer, DateRangeBound bound) { + buffer.putLong(bound.getTimestamp().toInstant().toEpochMilli()); + buffer.put(bound.getPrecision().getEncoding()); + } + + @Nullable + @Override + public DateRange decode(@Nullable ByteBuffer bytes, @NonNull ProtocolVersion protocolVersion) { + if (bytes == null || bytes.remaining() == 0) { + return null; + } + byte type = bytes.get(); + switch (type) { + case DATE_RANGE_TYPE_SINGLE_DATE: + return new DateRange(decodeLowerBound(bytes)); + case DATE_RANGE_TYPE_CLOSED_RANGE: + return new DateRange(decodeLowerBound(bytes), decodeUpperBound(bytes)); + case DATE_RANGE_TYPE_OPEN_RANGE_HIGH: + return new DateRange(decodeLowerBound(bytes), DateRangeBound.UNBOUNDED); + case DATE_RANGE_TYPE_OPEN_RANGE_LOW: + return new DateRange(DateRangeBound.UNBOUNDED, decodeUpperBound(bytes)); + case DATE_RANGE_TYPE_BOTH_OPEN_RANGE: + return new DateRange(DateRangeBound.UNBOUNDED, DateRangeBound.UNBOUNDED); + case DATE_RANGE_TYPE_SINGLE_DATE_OPEN: + return new DateRange(DateRangeBound.UNBOUNDED); + default: + throw new IllegalArgumentException("Unknown date range type: " + type); + } + } + + private static DateRangeBound decodeLowerBound(ByteBuffer bytes) { + long epochMilli = bytes.getLong(); + ZonedDateTime timestamp = + ZonedDateTime.ofInstant(Instant.ofEpochMilli(epochMilli), ZoneOffset.UTC); + DateRangePrecision precision = DateRangePrecision.fromEncoding(bytes.get()); + return DateRangeBound.lowerBound(timestamp, precision); + } + + private static DateRangeBound decodeUpperBound(ByteBuffer bytes) { + long epochMilli = bytes.getLong(); + ZonedDateTime timestamp = + ZonedDateTime.ofInstant(Instant.ofEpochMilli(epochMilli), ZoneOffset.UTC); + DateRangePrecision precision = DateRangePrecision.fromEncoding(bytes.get()); + return DateRangeBound.upperBound(timestamp, precision); + } + + @NonNull + @Override + public String format(@Nullable DateRange dateRange) { + return (dateRange == null) ? "NULL" : Strings.quote(dateRange.toString()); + } + + @Nullable + @Override + public DateRange parse(@Nullable String value) { + if (value == null || value.isEmpty() || value.equalsIgnoreCase("NULL")) { + return null; + } + try { + return DateRange.parse(Strings.unquote(value)); + } catch (ParseException e) { + throw new IllegalArgumentException(String.format("Invalid date range literal: %s", value), e); + } + } +} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/util/concurrent/BoundedConcurrentQueue.java b/core/src/main/java/com/datastax/dse/driver/internal/core/util/concurrent/BoundedConcurrentQueue.java new file mode 100644 index 00000000000..ea9ccd7d622 --- /dev/null +++ b/core/src/main/java/com/datastax/dse/driver/internal/core/util/concurrent/BoundedConcurrentQueue.java @@ -0,0 +1,134 @@ +/* + * 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 com.datastax.dse.driver.internal.core.util.concurrent; + +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.util.Deque; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.ConcurrentLinkedDeque; +import java.util.concurrent.atomic.AtomicReference; + +/** + * A concurrent queue with a limited size. + * + *

Once the queue is full, the insertion of the next element is delayed until space becomes + * available again; in the meantime, additional insertions are not allowed (in other words, there + * can be at most one "pending" element waiting on a full queue). + */ +public class BoundedConcurrentQueue { + + private final Deque elements = new ConcurrentLinkedDeque<>(); + private final AtomicReference state; + + public BoundedConcurrentQueue(int maxSize) { + this.state = new AtomicReference<>(new State(maxSize)); + } + + /** + * @return a stage that completes when the element is inserted. If there was still space in the + * queue, it will be already complete; if the queue was full, it will complete at a later + * point in time (triggered by a call to {@link #poll()}). This method must not be invoked + * again until the stage has completed. + * @throws IllegalStateException if the method is invoked before the stage returned by the + * previous call has completed. + */ + @NonNull + public CompletionStage offer(@NonNull ElementT element) { + while (true) { + State oldState = state.get(); + State newState = oldState.increment(); + if (state.compareAndSet(oldState, newState)) { + if (newState.spaceAvailable != null) { + return newState.spaceAvailable.thenApply( + (aVoid) -> { + elements.offer(element); + return element; + }); + } else { + elements.offer(element); + return CompletableFuture.completedFuture(element); + } + } + } + } + + @Nullable + public ElementT poll() { + while (true) { + State oldState = state.get(); + if (oldState.size == 0) { + return null; + } + State newState = oldState.decrement(); + if (state.compareAndSet(oldState, newState)) { + if (oldState.spaceAvailable != null) { + oldState.spaceAvailable.complete(null); + } + return elements.poll(); + } + } + } + + @Nullable + public ElementT peek() { + return elements.peek(); + } + + /** + * Note that this does not complete a pending call to {@link #offer(Object)}. We only use this + * method for terminal states where we want to dereference the contained elements. + */ + public void clear() { + elements.clear(); + } + + private static class State { + + private final int maxSize; + + final int size; // Number of elements in the queue, + 1 if one is waiting to get in + final CompletableFuture spaceAvailable; // Not null iff size == maxSize + 1 + + State(int maxSize) { + this(0, null, maxSize); + } + + private State(int size, CompletableFuture spaceAvailable, int maxSize) { + this.maxSize = maxSize; + this.size = size; + this.spaceAvailable = spaceAvailable; + } + + State increment() { + if (size > maxSize) { + throw new IllegalStateException( + "Can't call offer() until the stage returned by the previous offer() call has completed"); + } + int newSize = size + 1; + CompletableFuture newFuture = + (newSize == maxSize + 1) ? new CompletableFuture<>() : null; + return new State(newSize, newFuture, maxSize); + } + + State decrement() { + return new State(size - 1, null, maxSize); + } + } +} diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/AllNodesFailedException.java b/core/src/main/java/com/datastax/oss/driver/api/core/AllNodesFailedException.java index a897c4d9e27..b6f1bf93838 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/AllNodesFailedException.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/AllNodesFailedException.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -17,87 +19,169 @@ import com.datastax.oss.driver.api.core.cql.ExecutionInfo; import com.datastax.oss.driver.api.core.metadata.Node; -import com.datastax.oss.driver.shaded.guava.common.base.Joiner; +import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableList; import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableMap; import com.datastax.oss.driver.shaded.guava.common.collect.Iterables; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; /** * Thrown when a query failed on all the coordinators it was tried on. This exception may wrap - * multiple errors, use {@link #getErrors()} to inspect the individual problem on each node. + * multiple errors, that are available either as {@linkplain #getSuppressed() suppressed + * exceptions}, or via {@link #getAllErrors()} where they are grouped by node. */ public class AllNodesFailedException extends DriverException { + /** @deprecated Use {@link #fromErrors(List)} instead. */ @NonNull + @Deprecated public static AllNodesFailedException fromErrors(@Nullable Map errors) { if (errors == null || errors.isEmpty()) { return new NoNodeAvailableException(); } else { - return new AllNodesFailedException(ImmutableMap.copyOf(errors)); + return new AllNodesFailedException(groupByNode(errors)); } } @NonNull - public static AllNodesFailedException fromErrors( - @Nullable List> errors) { - Map map; + public static AllNodesFailedException fromErrors(@Nullable List> errors) { if (errors == null || errors.isEmpty()) { - map = null; + return new NoNodeAvailableException(); } else { - ImmutableMap.Builder builder = ImmutableMap.builder(); - for (Map.Entry entry : errors) { - builder.put(entry); - } - map = builder.build(); + return new AllNodesFailedException(groupByNode(errors)); } - return fromErrors(map); } - private final Map errors; + private final Map> errors; + /** @deprecated Use {@link #AllNodesFailedException(String, ExecutionInfo, Iterable)} instead. */ + @Deprecated protected AllNodesFailedException( @NonNull String message, @Nullable ExecutionInfo executionInfo, @NonNull Map errors) { super(message, executionInfo, null, true); - this.errors = errors; + this.errors = toDeepImmutableMap(groupByNode(errors)); + addSuppressedErrors(); + } + + protected AllNodesFailedException( + @NonNull String message, + @Nullable ExecutionInfo executionInfo, + @NonNull Iterable>> errors) { + super(message, executionInfo, null, true); + this.errors = toDeepImmutableMap(errors); + addSuppressedErrors(); + } + + private void addSuppressedErrors() { + for (List errors : this.errors.values()) { + for (Throwable error : errors) { + addSuppressed(error); + } + } } - private AllNodesFailedException(Map errors) { + private AllNodesFailedException(Map> errors) { this( buildMessage( String.format("All %d node(s) tried for the query failed", errors.size()), errors), null, - errors); + errors.entrySet()); } - private static String buildMessage(String baseMessage, Map errors) { + private static String buildMessage(String baseMessage, Map> errors) { int limit = Math.min(errors.size(), 3); - String details = - Joiner.on(", ").withKeyValueSeparator(": ").join(Iterables.limit(errors.entrySet(), limit)); - + Iterator>> iterator = + Iterables.limit(errors.entrySet(), limit).iterator(); + StringBuilder details = new StringBuilder(); + while (iterator.hasNext()) { + Entry> entry = iterator.next(); + details.append(entry.getKey()).append(": ").append(entry.getValue()); + if (iterator.hasNext()) { + details.append(", "); + } + } return String.format( - baseMessage + " (showing first %d, use getErrors() for more: %s)", limit, details); + "%s (showing first %d nodes, use getAllErrors() for more): %s", + baseMessage, limit, details); } - /** The details of the individual error on each node. */ + /** + * An immutable map containing the first error on each tried node. + * + * @deprecated Use {@link #getAllErrors()} instead. + */ @NonNull + @Deprecated public Map getErrors() { + ImmutableMap.Builder builder = ImmutableMap.builder(); + for (Node node : errors.keySet()) { + List nodeErrors = errors.get(node); + if (!nodeErrors.isEmpty()) { + builder.put(node, nodeErrors.get(0)); + } + } + return builder.build(); + } + + /** An immutable map containing all errors on each tried node. */ + @NonNull + public Map> getAllErrors() { return errors; } @NonNull @Override public DriverException copy() { - return new AllNodesFailedException(getMessage(), getExecutionInfo(), errors); + return new AllNodesFailedException(getMessage(), getExecutionInfo(), errors.entrySet()); } @NonNull public AllNodesFailedException reword(String newMessage) { return new AllNodesFailedException( - buildMessage(newMessage, errors), getExecutionInfo(), errors); + buildMessage(newMessage, errors), getExecutionInfo(), errors.entrySet()); + } + + private static Map> groupByNode(Map errors) { + return groupByNode(errors.entrySet()); + } + + private static Map> groupByNode(Iterable> errors) { + // no need for immutable collections here + Map> map = new LinkedHashMap<>(); + for (Entry entry : errors) { + Node node = entry.getKey(); + Throwable error = entry.getValue(); + map.compute( + node, + (k, v) -> { + if (v == null) { + v = new ArrayList<>(); + } + v.add(error); + return v; + }); + } + return map; + } + + private static Map> toDeepImmutableMap(Map> errors) { + return toDeepImmutableMap(errors.entrySet()); + } + + private static Map> toDeepImmutableMap( + Iterable>> errors) { + ImmutableMap.Builder> builder = ImmutableMap.builder(); + for (Entry> entry : errors) { + builder.put(entry.getKey(), ImmutableList.copyOf(entry.getValue())); + } + return builder.build(); } } diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/AsyncAutoCloseable.java b/core/src/main/java/com/datastax/oss/driver/api/core/AsyncAutoCloseable.java index f84cdf26c86..7f8cafbc895 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/AsyncAutoCloseable.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/AsyncAutoCloseable.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/AsyncPagingIterable.java b/core/src/main/java/com/datastax/oss/driver/api/core/AsyncPagingIterable.java index 9abe4136f66..fd7c5be6baa 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/AsyncPagingIterable.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/AsyncPagingIterable.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/ConsistencyLevel.java b/core/src/main/java/com/datastax/oss/driver/api/core/ConsistencyLevel.java index 65e32308fca..a1b6d8006df 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/ConsistencyLevel.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/ConsistencyLevel.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -26,6 +28,18 @@ */ public interface ConsistencyLevel { + ConsistencyLevel ANY = DefaultConsistencyLevel.ANY; + ConsistencyLevel ONE = DefaultConsistencyLevel.ONE; + ConsistencyLevel TWO = DefaultConsistencyLevel.TWO; + ConsistencyLevel THREE = DefaultConsistencyLevel.THREE; + ConsistencyLevel QUORUM = DefaultConsistencyLevel.QUORUM; + ConsistencyLevel ALL = DefaultConsistencyLevel.ALL; + ConsistencyLevel LOCAL_ONE = DefaultConsistencyLevel.LOCAL_ONE; + ConsistencyLevel LOCAL_QUORUM = DefaultConsistencyLevel.LOCAL_QUORUM; + ConsistencyLevel EACH_QUORUM = DefaultConsistencyLevel.EACH_QUORUM; + ConsistencyLevel SERIAL = DefaultConsistencyLevel.SERIAL; + ConsistencyLevel LOCAL_SERIAL = DefaultConsistencyLevel.LOCAL_SERIAL; + /** The numerical value that the level is encoded to in protocol frames. */ int getProtocolCode(); diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/CqlIdentifier.java b/core/src/main/java/com/datastax/oss/driver/api/core/CqlIdentifier.java index 89211d75382..82e4c2b30a6 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/CqlIdentifier.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/CqlIdentifier.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -21,6 +23,7 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.Serializable; +import java.util.Locale; import net.jcip.annotations.Immutable; /** @@ -75,7 +78,7 @@ public static CqlIdentifier fromCql(@NonNull String cql) { if (Strings.isDoubleQuoted(cql)) { internal = Strings.unDoubleQuote(cql); } else { - internal = cql.toLowerCase(); + internal = cql.toLowerCase(Locale.ROOT); Preconditions.checkArgument( !Strings.needsDoubleQuotes(internal), "Invalid CQL form [%s]: needs double quotes", cql); } diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/CqlSession.java b/core/src/main/java/com/datastax/oss/driver/api/core/CqlSession.java index 04a98054dc0..ff096719f3e 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/CqlSession.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/CqlSession.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -15,218 +17,54 @@ */ package com.datastax.oss.driver.api.core; -import com.datastax.oss.driver.api.core.cql.AsyncResultSet; -import com.datastax.oss.driver.api.core.cql.PrepareRequest; -import com.datastax.oss.driver.api.core.cql.PreparedStatement; -import com.datastax.oss.driver.api.core.cql.ResultSet; -import com.datastax.oss.driver.api.core.cql.SimpleStatement; -import com.datastax.oss.driver.api.core.cql.Statement; -import com.datastax.oss.driver.api.core.session.Request; +import com.datastax.dse.driver.api.core.cql.continuous.ContinuousSession; +import com.datastax.dse.driver.api.core.cql.continuous.reactive.ContinuousReactiveSession; +import com.datastax.dse.driver.api.core.cql.reactive.ReactiveSession; +import com.datastax.dse.driver.api.core.graph.GraphSession; +import com.datastax.dse.driver.api.core.graph.reactive.ReactiveGraphSession; +import com.datastax.oss.driver.api.core.cql.AsyncCqlSession; +import com.datastax.oss.driver.api.core.cql.SyncCqlSession; import com.datastax.oss.driver.api.core.session.Session; -import com.datastax.oss.driver.internal.core.cql.DefaultPrepareRequest; import edu.umd.cs.findbugs.annotations.NonNull; -import java.util.Objects; -import java.util.concurrent.CompletionStage; - -/** A specialized session with convenience methods to execute CQL statements. */ -public interface CqlSession extends Session { - - /** Returns a builder to create a new instance. */ - @NonNull - static CqlSessionBuilder builder() { - return new CqlSessionBuilder(); - } - - /** - * Executes a CQL statement synchronously (the calling thread blocks until the result becomes - * available). - */ - @NonNull - default ResultSet execute(@NonNull Statement statement) { - return Objects.requireNonNull( - execute(statement, Statement.SYNC), "The CQL processor should never return a null result"); - } - - /** - * Executes a CQL statement synchronously (the calling thread blocks until the result becomes - * available). - */ - @NonNull - default ResultSet execute(@NonNull String query) { - return execute(SimpleStatement.newInstance(query)); - } - - /** - * Executes a CQL statement asynchronously (the call returns as soon as the statement was sent, - * generally before the result is available). - */ - @NonNull - default CompletionStage executeAsync(@NonNull Statement statement) { - return Objects.requireNonNull( - execute(statement, Statement.ASYNC), "The CQL processor should never return a null result"); - } - - /** - * Executes a CQL statement asynchronously (the call returns as soon as the statement was sent, - * generally before the result is available). - */ - @NonNull - default CompletionStage executeAsync(@NonNull String query) { - return executeAsync(SimpleStatement.newInstance(query)); - } - /** - * Prepares a CQL statement synchronously (the calling thread blocks until the statement is - * prepared). - * - *

Note that the bound statements created from the resulting prepared statement will inherit - * some of the attributes of the provided simple statement. That is, given: - * - *

{@code
-   * SimpleStatement simpleStatement = SimpleStatement.newInstance("...");
-   * PreparedStatement preparedStatement = session.prepare(simpleStatement);
-   * BoundStatement boundStatement = preparedStatement.bind();
-   * }
- * - * Then: - * - *
    - *
  • the following methods return the same value as their counterpart on {@code - * simpleStatement}: - *
      - *
    • {@link Request#getExecutionProfileName() boundStatement.getExecutionProfileName()} - *
    • {@link Request#getExecutionProfile() boundStatement.getExecutionProfile()} - *
    • {@link Statement#getPagingState() boundStatement.getPagingState()} - *
    • {@link Request#getRoutingKey() boundStatement.getRoutingKey()} - *
    • {@link Request#getRoutingToken() boundStatement.getRoutingToken()} - *
    • {@link Request#getCustomPayload() boundStatement.getCustomPayload()} - *
    • {@link Request#isIdempotent() boundStatement.isIdempotent()} - *
    • {@link Request#getTimeout() boundStatement.getTimeout()} - *
    • {@link Statement#getPagingState() boundStatement.getPagingState()} - *
    • {@link Statement#getPageSize() boundStatement.getPageSize()} - *
    • {@link Statement#getConsistencyLevel() boundStatement.getConsistencyLevel()} - *
    • {@link Statement#getSerialConsistencyLevel() - * boundStatement.getSerialConsistencyLevel()} - *
    • {@link Statement#isTracing() boundStatement.isTracing()} - *
    - *
  • {@link Request#getRoutingKeyspace() boundStatement.getRoutingKeyspace()} is set from - * either {@link Request#getKeyspace() simpleStatement.getKeyspace()} (if it's not {@code - * null}), or {@code simpleStatement.getRoutingKeyspace()}; - *
  • on the other hand, the following attributes are not propagated: - *
      - *
    • {@link Statement#getQueryTimestamp() boundStatement.getQueryTimestamp()} will be - * set to {@link Long#MIN_VALUE}, meaning that the value will be assigned by the - * session's timestamp generator. - *
    • {@link Statement#getNode() boundStatement.getNode()} will always be {@code null}. - *
    - *
- * - * If you want to customize this behavior, you can write your own implementation of {@link - * PrepareRequest} and pass it to {@link #prepare(PrepareRequest)}. - * - *

The result of this method is cached: if you call it twice with the same {@link - * SimpleStatement}, you will get the same {@link PreparedStatement} instance. We still recommend - * keeping a reference to it (for example by caching it as a field in a DAO); if that's not - * possible (e.g. if query strings are generated dynamically), it's OK to call this method every - * time: there will just be a small performance overhead to check the internal cache. Note that - * caching is based on: - * - *

    - *
  • the query string exactly as you provided it: the driver does not perform any kind of - * trimming or sanitizing. - *
  • all other execution parameters: for example, preparing two statements with identical - * query strings but different {@linkplain SimpleStatement#getConsistencyLevel() consistency - * levels} will yield distinct prepared statements. - *
- */ - @NonNull - default PreparedStatement prepare(@NonNull SimpleStatement statement) { - return Objects.requireNonNull( - execute(new DefaultPrepareRequest(statement), PrepareRequest.SYNC), - "The CQL prepare processor should never return a null result"); - } - - /** - * Prepares a CQL statement synchronously (the calling thread blocks until the statement is - * prepared). - * - *

The result of this method is cached (see {@link #prepare(SimpleStatement)} for more - * explanations). - */ - @NonNull - default PreparedStatement prepare(@NonNull String query) { - return Objects.requireNonNull( - execute(new DefaultPrepareRequest(query), PrepareRequest.SYNC), - "The CQL prepare processor should never return a null result"); - } - - /** - * Prepares a CQL statement synchronously (the calling thread blocks until the statement is - * prepared). - * - *

This variant is exposed in case you use an ad hoc {@link PrepareRequest} implementation to - * customize how attributes are propagated when you prepare a {@link SimpleStatement} (see {@link - * #prepare(SimpleStatement)} for more explanations). Otherwise, you should rarely have to deal - * with {@link PrepareRequest} directly. - * - *

The result of this method is cached (see {@link #prepare(SimpleStatement)} for more - * explanations). - */ - @NonNull - default PreparedStatement prepare(@NonNull PrepareRequest request) { - return Objects.requireNonNull( - execute(request, PrepareRequest.SYNC), - "The CQL prepare processor should never return a null result"); - } - - /** - * Prepares a CQL statement asynchronously (the call returns as soon as the prepare query was - * sent, generally before the statement is prepared). - * - *

Note that the bound statements created from the resulting prepared statement will inherit - * some of the attributes of {@code query}; see {@link #prepare(SimpleStatement)} for more - * details. - * - *

The result of this method is cached (see {@link #prepare(SimpleStatement)} for more - * explanations). - */ - @NonNull - default CompletionStage prepareAsync(@NonNull SimpleStatement statement) { - return Objects.requireNonNull( - execute(new DefaultPrepareRequest(statement), PrepareRequest.ASYNC), - "The CQL prepare processor should never return a null result"); - } - - /** - * Prepares a CQL statement asynchronously (the call returns as soon as the prepare query was - * sent, generally before the statement is prepared). - * - *

The result of this method is cached (see {@link #prepare(SimpleStatement)} for more - * explanations). - */ - @NonNull - default CompletionStage prepareAsync(@NonNull String query) { - return Objects.requireNonNull( - execute(new DefaultPrepareRequest(query), PrepareRequest.ASYNC), - "The CQL prepare processor should never return a null result"); - } +/** + * The default session type built by the driver. + * + *

It provides user-friendly execution methods for: + * + *

    + *
  • CQL requests: synchronous, asynchronous or reactive mode; + *
  • requests specific to DataStax Enterprise: graph and continuous paging. + *
+ * + * Client applications can use this interface even if they don't need all the features. In + * particular, it can be used with a regular Apache Cassandra ® cluster, as long as you don't + * call any of the DSE-specific execute methods. If you're in that situation, you might also want to + * exclude certain dependencies from your classpath (see the "Integration" page in the user manual). + * + *

Note that the name "CQL session" is no longer really accurate since this interface can now + * execute other request types; but it was preserved for backward compatibility with previous driver + * versions. + */ +public interface CqlSession + extends Session, + SyncCqlSession, + AsyncCqlSession, + ReactiveSession, + ContinuousSession, + GraphSession, + ContinuousReactiveSession, + ReactiveGraphSession { /** - * Prepares a CQL statement asynchronously (the call returns as soon as the prepare query was - * sent, generally before the statement is prepared). + * Returns a builder to create a new instance. * - *

This variant is exposed in case you use an ad hoc {@link PrepareRequest} implementation to - * customize how attributes are propagated when you prepare a {@link SimpleStatement} (see {@link - * #prepare(SimpleStatement)} for more explanations). Otherwise, you should rarely have to deal - * with {@link PrepareRequest} directly. + *

Note that this builder is mutable and not thread-safe. * - *

The result of this method is cached (see {@link #prepare(SimpleStatement)} for more - * explanations). + * @return {@code CqlSessionBuilder} to create a new instance. */ @NonNull - default CompletionStage prepareAsync(PrepareRequest request) { - return Objects.requireNonNull( - execute(request, PrepareRequest.ASYNC), - "The CQL prepare processor should never return a null result"); + static CqlSessionBuilder builder() { + return new CqlSessionBuilder(); } } diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/CqlSessionBuilder.java b/core/src/main/java/com/datastax/oss/driver/api/core/CqlSessionBuilder.java index 064b6b12779..4598c078dca 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/CqlSessionBuilder.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/CqlSessionBuilder.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -19,7 +21,11 @@ import edu.umd.cs.findbugs.annotations.NonNull; import net.jcip.annotations.NotThreadSafe; -/** Helper class to build a {@link CqlSession} instance. */ +/** + * Helper class to build a {@link CqlSession} instance. + * + *

This class is mutable and not thread-safe. + */ @NotThreadSafe public class CqlSessionBuilder extends SessionBuilder { diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/DefaultConsistencyLevel.java b/core/src/main/java/com/datastax/oss/driver/api/core/DefaultConsistencyLevel.java index 34d8875eb8e..2e5a4a6f022 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/DefaultConsistencyLevel.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/DefaultConsistencyLevel.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -35,6 +37,9 @@ public enum DefaultConsistencyLevel implements ConsistencyLevel { SERIAL(ProtocolConstants.ConsistencyLevel.SERIAL), LOCAL_SERIAL(ProtocolConstants.ConsistencyLevel.LOCAL_SERIAL), ; + // Note that, for the sake of convenience, we also expose shortcuts to these constants on the + // ConsistencyLevel interface. If you add a new enum constant, remember to update the interface as + // well. private final int protocolCode; @@ -66,7 +71,7 @@ public boolean isSerial() { return this == SERIAL || this == LOCAL_SERIAL; } - private static Map BY_CODE = mapByCode(values()); + private static final Map BY_CODE = mapByCode(values()); private static Map mapByCode(DefaultConsistencyLevel[] levels) { ImmutableMap.Builder builder = ImmutableMap.builder(); diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/DefaultProtocolVersion.java b/core/src/main/java/com/datastax/oss/driver/api/core/DefaultProtocolVersion.java index feda0c2afc8..91b45fc506a 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/DefaultProtocolVersion.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/DefaultProtocolVersion.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -30,14 +32,21 @@ public enum DefaultProtocolVersion implements ProtocolVersion { /** Version 4, supported by Cassandra 2.2 and above. */ V4(ProtocolConstants.Version.V4, false), + /** Version 5, supported by Cassandra 4.0 and above. */ + V5(ProtocolConstants.Version.V5, false), + /** - * Version 5, currently supported as a beta preview in Cassandra 3.10 and above. + * Version 6, currently supported as a beta preview in Cassandra 4.0 and above. * *

Do not use this in production. * * @see ProtocolVersion#isBeta() */ - V5(ProtocolConstants.Version.V5, true); + V6(ProtocolConstants.Version.V6, true), + ; + // Note that, for the sake of convenience, we also expose shortcuts to these constants on the + // ProtocolVersion interface. If you add a new enum constant, remember to update the interface as + // well. private final int code; private final boolean beta; diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/DriverException.java b/core/src/main/java/com/datastax/oss/driver/api/core/DriverException.java index 07f79d6e341..f5cf76e29eb 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/DriverException.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/DriverException.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -39,7 +41,7 @@ */ public abstract class DriverException extends RuntimeException { - private volatile ExecutionInfo executionInfo; + private transient volatile ExecutionInfo executionInfo; protected DriverException( @Nullable String message, @@ -74,8 +76,14 @@ protected DriverException( * *

Note that this is only set for exceptions that are rethrown directly to the client from a * session call. For example, individual node errors stored in {@link - * AllNodesFailedException#getErrors()} or {@link ExecutionInfo#getErrors()} do not contain their - * own execution info, and therefore return null from this method. + * AllNodesFailedException#getAllErrors()} or {@link ExecutionInfo#getErrors()} do not contain + * their own execution info, and therefore return null from this method. + * + *

This method will also return null for low-level exceptions thrown directly from a driver + * channel, such as {@link com.datastax.oss.driver.api.core.connection.ConnectionInitException} or + * {@link com.datastax.oss.driver.api.core.connection.ClosedConnectionException}. + * + *

It will also be null if you serialize and deserialize an exception. */ public ExecutionInfo getExecutionInfo() { return executionInfo; diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/DriverExecutionException.java b/core/src/main/java/com/datastax/oss/driver/api/core/DriverExecutionException.java index e7d8e42f2b1..90ff875e375 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/DriverExecutionException.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/DriverExecutionException.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/DriverTimeoutException.java b/core/src/main/java/com/datastax/oss/driver/api/core/DriverTimeoutException.java index 1966606256b..8b4cc5dc5bb 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/DriverTimeoutException.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/DriverTimeoutException.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/InvalidKeyspaceException.java b/core/src/main/java/com/datastax/oss/driver/api/core/InvalidKeyspaceException.java index 0b1ec172812..aa3f774800c 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/InvalidKeyspaceException.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/InvalidKeyspaceException.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/MappedAsyncPagingIterable.java b/core/src/main/java/com/datastax/oss/driver/api/core/MappedAsyncPagingIterable.java index c6cb7ae5831..b3902489a48 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/MappedAsyncPagingIterable.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/MappedAsyncPagingIterable.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/MavenCoordinates.java b/core/src/main/java/com/datastax/oss/driver/api/core/MavenCoordinates.java index ab26be868dd..3c3f18a5dc2 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/MavenCoordinates.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/MavenCoordinates.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/NoNodeAvailableException.java b/core/src/main/java/com/datastax/oss/driver/api/core/NoNodeAvailableException.java index db231adf219..9ef51fb99b6 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/NoNodeAvailableException.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/NoNodeAvailableException.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -31,7 +33,7 @@ public NoNodeAvailableException() { } private NoNodeAvailableException(ExecutionInfo executionInfo) { - super("No node was available to execute the query", executionInfo, Collections.emptyMap()); + super("No node was available to execute the query", executionInfo, Collections.emptySet()); } @NonNull diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/NodeUnavailableException.java b/core/src/main/java/com/datastax/oss/driver/api/core/NodeUnavailableException.java new file mode 100644 index 00000000000..5303119844e --- /dev/null +++ b/core/src/main/java/com/datastax/oss/driver/api/core/NodeUnavailableException.java @@ -0,0 +1,62 @@ +/* + * 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 com.datastax.oss.driver.api.core; + +import com.datastax.oss.driver.api.core.metadata.Node; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.Objects; + +/** + * Indicates that a {@link Node} was selected in a query plan, but it had no connection available. + * + *

A common reason to encounter this error is when the configured number of connections per node + * and requests per connection is not high enough to absorb the overall request rate. This can be + * mitigated by tuning the following options: + * + *

    + *
  • {@code advanced.connection.pool.local.size}; + *
  • {@code advanced.connection.pool.remote.size}; + *
  • {@code advanced.connection.max-requests-per-connection}. + *
+ * + * See {@code reference.conf} for more details. + * + *

Another possibility is when you are trying to direct a request {@linkplain + * com.datastax.oss.driver.api.core.cql.Statement#setNode(Node) to a particular node}, but that node + * has no connections available. + */ +public class NodeUnavailableException extends DriverException { + + private final Node node; + + public NodeUnavailableException(Node node) { + super("No connection was available to " + node, null, null, true); + this.node = Objects.requireNonNull(node); + } + + @NonNull + public Node getNode() { + return node; + } + + @Override + @NonNull + public DriverException copy() { + return new NodeUnavailableException(node); + } +} diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/PagingIterable.java b/core/src/main/java/com/datastax/oss/driver/api/core/PagingIterable.java index 0a7f4768de4..c2a81b554d0 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/PagingIterable.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/PagingIterable.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -19,6 +21,7 @@ import com.datastax.oss.driver.api.core.cql.ExecutionInfo; import com.datastax.oss.driver.api.core.cql.ResultSet; import com.datastax.oss.driver.internal.core.PagingIterableWrapper; +import com.datastax.oss.driver.internal.core.cql.PagingIterableSpliterator; import com.datastax.oss.driver.shaded.guava.common.collect.Iterables; import com.datastax.oss.driver.shaded.guava.common.collect.Lists; import edu.umd.cs.findbugs.annotations.NonNull; @@ -26,6 +29,7 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; +import java.util.Spliterator; import java.util.function.Function; /** @@ -100,6 +104,7 @@ default ElementT one() { * reasonable number of results. */ @NonNull + @SuppressWarnings("MixedMutabilityReturnType") default List all() { if (!iterator().hasNext()) { return Collections.emptyList(); @@ -154,8 +159,28 @@ default List all() { *

Note that both instances share the same underlying data: consuming elements from the * transformed iterable will also consume them from this object, and vice-versa. */ + @NonNull default PagingIterable map( Function elementMapper) { return new PagingIterableWrapper<>(this, elementMapper); } + + /** + * {@inheritDoc} + * + *

Default spliterators created by the driver will report the following characteristics: {@link + * Spliterator#ORDERED}, {@link Spliterator#IMMUTABLE}, {@link Spliterator#NONNULL}. Single-page + * result sets will also report {@link Spliterator#SIZED} and {@link Spliterator#SUBSIZED}, since + * the result set size is known. + * + *

This method should be called at most once. Spliterators share the same underlying data but + * do not support concurrent consumption; once a spliterator for this iterable is obtained, the + * iterable should not be consumed through calls to other methods such as {@link + * #iterator()}, {@link #one()} or {@link #all()}; doing so will result in unpredictable results. + */ + @NonNull + @Override + default Spliterator spliterator() { + return new PagingIterableSpliterator<>(this); + } } diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/ProtocolVersion.java b/core/src/main/java/com/datastax/oss/driver/api/core/ProtocolVersion.java index e39837cc090..dd69f705453 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/ProtocolVersion.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/ProtocolVersion.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -15,6 +17,7 @@ */ package com.datastax.oss.driver.api.core; +import com.datastax.dse.driver.api.core.DseProtocolVersion; import com.datastax.oss.driver.api.core.detach.Detachable; import edu.umd.cs.findbugs.annotations.NonNull; @@ -26,10 +29,18 @@ * {@code ProtocolVersion}s are {@link DefaultProtocolVersion} instances. */ public interface ProtocolVersion { + + ProtocolVersion V3 = DefaultProtocolVersion.V3; + ProtocolVersion V4 = DefaultProtocolVersion.V4; + ProtocolVersion V5 = DefaultProtocolVersion.V5; + ProtocolVersion V6 = DefaultProtocolVersion.V6; + ProtocolVersion DSE_V1 = DseProtocolVersion.DSE_V1; + ProtocolVersion DSE_V2 = DseProtocolVersion.DSE_V2; + /** The default version used for {@link Detachable detached} objects. */ // Implementation note: we can't use the ProtocolVersionRegistry here, this has to be a // compile-time constant. - ProtocolVersion DEFAULT = DefaultProtocolVersion.V4; + ProtocolVersion DEFAULT = DefaultProtocolVersion.V5; /** * A numeric code that uniquely identifies the version (this is the code used in network frames). diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/RequestThrottlingException.java b/core/src/main/java/com/datastax/oss/driver/api/core/RequestThrottlingException.java index 5bd44fcb1d2..acf569d55f6 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/RequestThrottlingException.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/RequestThrottlingException.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/UnsupportedProtocolVersionException.java b/core/src/main/java/com/datastax/oss/driver/api/core/UnsupportedProtocolVersionException.java index d80eba55514..030984dc274 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/UnsupportedProtocolVersionException.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/UnsupportedProtocolVersionException.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/Version.java b/core/src/main/java/com/datastax/oss/driver/api/core/Version.java index f70db10c252..52751e02984 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/Version.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/Version.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -17,6 +19,7 @@ import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; +import java.io.Serializable; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -36,16 +39,25 @@ * are ignored for sorting versions. */ @Immutable -public class Version implements Comparable { +public class Version implements Comparable, Serializable { + + private static final long serialVersionUID = 1; private static final String VERSION_REGEXP = - "(\\d+)\\.(\\d+)(\\.\\d+)?(\\.\\d+)?([~\\-]\\w[.\\w]*(?:\\-\\w[.\\w]*)*)?(\\+[.\\w]+)?"; + "(\\d+)\\.(\\d+)(\\.\\d+)?(\\.\\d+)?([~\\-]\\w[.\\w]*(?:-\\w[.\\w]*)*)?(\\+[.\\w]+)?"; + private static final Pattern pattern = Pattern.compile(VERSION_REGEXP); - public static final Version V2_1_0 = parse("2.1.0"); - public static final Version V2_2_0 = parse("2.2.0"); - public static final Version V3_0_0 = parse("3.0.0"); - public static final Version V4_0_0 = parse("4.0.0"); + @NonNull public static final Version V1_0_0 = Objects.requireNonNull(parse("1.0.0")); + @NonNull public static final Version V2_1_0 = Objects.requireNonNull(parse("2.1.0")); + @NonNull public static final Version V2_2_0 = Objects.requireNonNull(parse("2.2.0")); + @NonNull public static final Version V3_0_0 = Objects.requireNonNull(parse("3.0.0")); + @NonNull public static final Version V4_0_0 = Objects.requireNonNull(parse("4.0.0")); + @NonNull public static final Version V4_1_0 = Objects.requireNonNull(parse("4.1.0")); + @NonNull public static final Version V5_0_0 = Objects.requireNonNull(parse("5.0.0")); + @NonNull public static final Version V6_7_0 = Objects.requireNonNull(parse("6.7.0")); + @NonNull public static final Version V6_8_0 = Objects.requireNonNull(parse("6.8.0")); + @NonNull public static final Version V6_9_0 = Objects.requireNonNull(parse("6.9.0")); private final int major; private final int minor; @@ -111,7 +123,7 @@ public static Version parse(@Nullable String version) { pr == null || pr.isEmpty() ? null : pr.substring(1) - .split("\\-"); // drop initial '-' or '~' then split on the remaining ones + .split("-"); // drop initial '-' or '~' then split on the remaining ones String bl = matcher.group(6); String build = bl == null || bl.isEmpty() ? null : bl.substring(1); // drop the initial '+' @@ -251,9 +263,7 @@ public int compareTo(@NonNull Version other) { } } - return preReleases.length == other.preReleases.length - ? 0 - : (preReleases.length < other.preReleases.length ? -1 : 1); + return Integer.compare(preReleases.length, other.preReleases.length); } @Override diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/addresstranslation/AddressTranslator.java b/core/src/main/java/com/datastax/oss/driver/api/core/addresstranslation/AddressTranslator.java index c80e16d3363..47ce62f1461 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/addresstranslation/AddressTranslator.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/addresstranslation/AddressTranslator.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/auth/AuthProvider.java b/core/src/main/java/com/datastax/oss/driver/api/core/auth/AuthProvider.java index e85b90e3b04..c73c3e4fb67 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/auth/AuthProvider.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/auth/AuthProvider.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/auth/AuthenticationException.java b/core/src/main/java/com/datastax/oss/driver/api/core/auth/AuthenticationException.java index abf77e293d5..28dde2123cb 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/auth/AuthenticationException.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/auth/AuthenticationException.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/auth/Authenticator.java b/core/src/main/java/com/datastax/oss/driver/api/core/auth/Authenticator.java index dd92762577e..150a1dfb63f 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/auth/Authenticator.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/auth/Authenticator.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -57,7 +59,11 @@ public interface Authenticator { * Obtain an initial response token for initializing the SASL handshake. * * @return a completion stage that will complete with the initial response to send to the server - * (which may be {@code null}). + * (which may be {@code null}). Note that, if the returned byte buffer is writable, the driver + * will clear its contents immediately after use (to avoid keeping sensitive + * information in memory); do not reuse the same buffer across multiple invocations. + * Alternatively, if the contents are not sensitive, you can make the buffer {@linkplain + * ByteBuffer#asReadOnlyBuffer() read-only} and safely reuse it. */ @NonNull CompletionStage initialResponse(); @@ -68,7 +74,11 @@ public interface Authenticator { * * @param challenge the server's SASL challenge. * @return a completion stage that will complete with the updated SASL token (which may be null to - * indicate the client requires no further action). + * indicate the client requires no further action). Note that, if the returned byte buffer is + * writable, the driver will clear its contents immediately after use (to avoid keeping + * sensitive information in memory); do not reuse the same buffer across multiple invocations. + * Alternatively, if the contents are not sensitive, you can make the buffer {@linkplain + * ByteBuffer#asReadOnlyBuffer() read-only} and safely reuse it. */ @NonNull CompletionStage evaluateChallenge(@Nullable ByteBuffer challenge); diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/auth/PlainTextAuthProviderBase.java b/core/src/main/java/com/datastax/oss/driver/api/core/auth/PlainTextAuthProviderBase.java new file mode 100644 index 00000000000..fb85797af9e --- /dev/null +++ b/core/src/main/java/com/datastax/oss/driver/api/core/auth/PlainTextAuthProviderBase.java @@ -0,0 +1,264 @@ +/* + * 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 com.datastax.oss.driver.api.core.auth; + +import com.datastax.dse.driver.api.core.auth.BaseDseAuthenticator; +import com.datastax.oss.driver.api.core.metadata.EndPoint; +import com.datastax.oss.driver.api.core.session.Session; +import com.datastax.oss.driver.shaded.guava.common.base.Charsets; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Objects; +import net.jcip.annotations.ThreadSafe; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Common infrastructure for plain text auth providers. + * + *

This can be reused to write an implementation that retrieves the credentials from another + * source than the configuration. The driver offers one built-in implementation: {@link + * ProgrammaticPlainTextAuthProvider}. + */ +@ThreadSafe +public abstract class PlainTextAuthProviderBase implements AuthProvider { + + private static final Logger LOG = LoggerFactory.getLogger(PlainTextAuthProviderBase.class); + + private final String logPrefix; + + /** + * @param logPrefix a string that will get prepended to the logs (this is used for discrimination + * when you have multiple driver instances executing in the same JVM). Built-in + * implementations fill this with {@link Session#getName()}. + */ + protected PlainTextAuthProviderBase(@NonNull String logPrefix) { + this.logPrefix = Objects.requireNonNull(logPrefix); + } + + /** + * Retrieves the credentials from the underlying source. + * + *

This is invoked every time the driver opens a new connection. + * + * @param endPoint The endpoint being contacted. + * @param serverAuthenticator The authenticator class sent by the endpoint. + */ + @NonNull + protected abstract Credentials getCredentials( + @NonNull EndPoint endPoint, @NonNull String serverAuthenticator); + + @NonNull + @Override + public Authenticator newAuthenticator( + @NonNull EndPoint endPoint, @NonNull String serverAuthenticator) + throws AuthenticationException { + return new PlainTextAuthenticator( + getCredentials(endPoint, serverAuthenticator), endPoint, serverAuthenticator); + } + + @Override + public void onMissingChallenge(@NonNull EndPoint endPoint) { + LOG.warn( + "[{}] {} did not send an authentication challenge; " + + "This is suspicious because the driver expects authentication", + logPrefix, + endPoint); + } + + @Override + public void close() { + // nothing to do + } + + public static class Credentials { + + private final char[] username; + private final char[] password; + private final char[] authorizationId; + + /** + * Builds an instance for username/password authentication, and proxy authentication with the + * given authorizationId. + * + *

This feature is only available with DataStax Enterprise. If the target server is Apache + * Cassandra, the authorizationId will be ignored. + */ + public Credentials( + @NonNull char[] username, @NonNull char[] password, @NonNull char[] authorizationId) { + this.username = Objects.requireNonNull(username); + this.password = Objects.requireNonNull(password); + this.authorizationId = Objects.requireNonNull(authorizationId); + } + + /** Builds an instance for simple username/password authentication. */ + public Credentials(@NonNull char[] username, @NonNull char[] password) { + this(username, password, new char[0]); + } + + @NonNull + public char[] getUsername() { + return username; + } + + /** + * @deprecated this method only exists for backward compatibility. It is a synonym for {@link + * #getUsername()}, which should be used instead. + */ + @Deprecated + @NonNull + public char[] getAuthenticationId() { + return username; + } + + @NonNull + public char[] getPassword() { + return password; + } + + @NonNull + public char[] getAuthorizationId() { + return authorizationId; + } + + /** Clears the credentials from memory when they're no longer needed. */ + protected void clear() { + // Note: this is a bit irrelevant with the built-in provider, because the config already + // caches the credentials in memory. But it might be useful for a custom implementation that + // retrieves the credentials from a different source. + Arrays.fill(getUsername(), (char) 0); + Arrays.fill(getPassword(), (char) 0); + Arrays.fill(getAuthorizationId(), (char) 0); + } + } + + // Implementation note: BaseDseAuthenticator is backward compatible with Cassandra authenticators. + // This will work with both Cassandra (as long as no authorizationId is set) and DSE. + protected static class PlainTextAuthenticator extends BaseDseAuthenticator { + + private static final ByteBuffer MECHANISM = + ByteBuffer.wrap("PLAIN".getBytes(StandardCharsets.UTF_8)).asReadOnlyBuffer(); + + private static final ByteBuffer SERVER_INITIAL_CHALLENGE = + ByteBuffer.wrap("PLAIN-START".getBytes(StandardCharsets.UTF_8)).asReadOnlyBuffer(); + + private static final EndPoint DUMMY_END_POINT = + new EndPoint() { + @NonNull + @Override + public SocketAddress resolve() { + return new InetSocketAddress("127.0.0.1", 9042); + } + + @NonNull + @Override + public String asMetricPrefix() { + return ""; // will never be used + } + }; + + private final ByteBuffer encodedCredentials; + private final EndPoint endPoint; + + protected PlainTextAuthenticator( + @NonNull Credentials credentials, + @NonNull EndPoint endPoint, + @NonNull String serverAuthenticator) { + super(serverAuthenticator); + + Objects.requireNonNull(credentials); + Objects.requireNonNull(endPoint); + + ByteBuffer authorizationId = toUtf8Bytes(credentials.getAuthorizationId()); + ByteBuffer username = toUtf8Bytes(credentials.getUsername()); + ByteBuffer password = toUtf8Bytes(credentials.getPassword()); + + this.encodedCredentials = + ByteBuffer.allocate( + authorizationId.remaining() + username.remaining() + password.remaining() + 2); + encodedCredentials.put(authorizationId); + encodedCredentials.put((byte) 0); + encodedCredentials.put(username); + encodedCredentials.put((byte) 0); + encodedCredentials.put(password); + encodedCredentials.flip(); + + clear(authorizationId); + clear(username); + clear(password); + + this.endPoint = endPoint; + } + + /** + * @deprecated Preserved for backward compatibility, implementors should use the 3-arg + * constructor {@code PlainTextAuthenticator(Credentials, EndPoint, String)} instead. + */ + @Deprecated + protected PlainTextAuthenticator(@NonNull Credentials credentials) { + this( + credentials, + // It's unlikely that this class was ever extended by third parties, but if it was, assume + // that it was not written for DSE: + // - dummy end point because we should never need to build an auth exception + DUMMY_END_POINT, + // - default OSS authenticator name (the only thing that matters is how this string + // compares to "DseAuthenticator") + "org.apache.cassandra.auth.PasswordAuthenticator"); + } + + private static ByteBuffer toUtf8Bytes(char[] charArray) { + CharBuffer charBuffer = CharBuffer.wrap(charArray); + return Charsets.UTF_8.encode(charBuffer); + } + + private static void clear(ByteBuffer buffer) { + buffer.rewind(); + while (buffer.remaining() > 0) { + buffer.put((byte) 0); + } + } + + @NonNull + @Override + public ByteBuffer getMechanism() { + return MECHANISM; + } + + @NonNull + @Override + public ByteBuffer getInitialServerChallenge() { + return SERVER_INITIAL_CHALLENGE; + } + + @Nullable + @Override + public ByteBuffer evaluateChallengeSync(@Nullable ByteBuffer challenge) { + if (SERVER_INITIAL_CHALLENGE.equals(challenge)) { + return encodedCredentials; + } + throw new AuthenticationException(endPoint, "Incorrect challenge from server"); + } + } +} diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/auth/ProgrammaticPlainTextAuthProvider.java b/core/src/main/java/com/datastax/oss/driver/api/core/auth/ProgrammaticPlainTextAuthProvider.java new file mode 100644 index 00000000000..d991f5c5cb5 --- /dev/null +++ b/core/src/main/java/com/datastax/oss/driver/api/core/auth/ProgrammaticPlainTextAuthProvider.java @@ -0,0 +1,136 @@ +/* + * 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 com.datastax.oss.driver.api.core.auth; + +import com.datastax.oss.driver.api.core.metadata.EndPoint; +import com.datastax.oss.driver.api.core.session.SessionBuilder; +import com.datastax.oss.driver.internal.core.util.Strings; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.Objects; +import net.jcip.annotations.ThreadSafe; + +/** + * A simple plaintext {@link AuthProvider} that receives the credentials programmatically instead of + * pulling them from the configuration. + * + *

To use this class, create an instance with the appropriate credentials to use and pass it to + * your session builder: + * + *

+ * AuthProvider authProvider = new ProgrammaticPlainTextAuthProvider("...", "...");
+ * CqlSession session =
+ *     CqlSession.builder()
+ *         .addContactEndPoints(...)
+ *         .withAuthProvider(authProvider)
+ *         .build();
+ * 
+ * + *

It also offers the possibility of changing the credentials at runtime. The new credentials + * will be used for all connections initiated after the change. + * + *

Implementation Note: this implementation is not particularly suited for highly-sensitive + * applications: it stores the credentials to use as private fields, and even if the fields are char + * arrays rather than strings to make it difficult to dump their contents, they are never cleared + * until the provider itself is garbage-collected, which typically only happens when the session is + * closed. + * + * @see SessionBuilder#withAuthProvider(AuthProvider) + * @see SessionBuilder#withAuthCredentials(String, String) + * @see SessionBuilder#withAuthCredentials(String, String, String) + */ +@ThreadSafe +public class ProgrammaticPlainTextAuthProvider extends PlainTextAuthProviderBase { + + private volatile char[] username; + private volatile char[] password; + private volatile char[] authorizationId; + + /** Builds an instance for simple username/password authentication. */ + public ProgrammaticPlainTextAuthProvider(@NonNull String username, @NonNull String password) { + this(username, password, ""); + } + + /** + * Builds an instance for username/password authentication, and proxy authentication with the + * given authorizationId. + * + *

This feature is only available with DataStax Enterprise. If the target server is Apache + * Cassandra, use {@link #ProgrammaticPlainTextAuthProvider(String, String)} instead, or set the + * authorizationId to an empty string. + */ + public ProgrammaticPlainTextAuthProvider( + @NonNull String username, @NonNull String password, @NonNull String authorizationId) { + // This will typically be built before the session so we don't know the log prefix yet. Pass an + // empty string, it's only used in one log message. + super(""); + this.username = Strings.requireNotEmpty(username, "username").toCharArray(); + this.password = Strings.requireNotEmpty(password, "password").toCharArray(); + this.authorizationId = + Objects.requireNonNull(authorizationId, "authorizationId cannot be null").toCharArray(); + } + + /** + * Changes the username. + * + *

The new credentials will be used for all connections initiated after this method was called. + * + * @param username the new name. + */ + public void setUsername(@NonNull String username) { + this.username = Strings.requireNotEmpty(username, "username").toCharArray(); + } + + /** + * Changes the password. + * + *

The new credentials will be used for all connections initiated after this method was called. + * + * @param password the new password. + */ + public void setPassword(@NonNull String password) { + this.password = Strings.requireNotEmpty(password, "password").toCharArray(); + } + + /** + * Changes the authorization id. + * + *

The new credentials will be used for all connections initiated after this method was called. + * + *

This feature is only available with DataStax Enterprise. If the target server is Apache + * Cassandra, this method should not be used. + * + * @param authorizationId the new authorization id. + */ + public void setAuthorizationId(@NonNull String authorizationId) { + this.authorizationId = + Objects.requireNonNull(authorizationId, "authorizationId cannot be null").toCharArray(); + } + + /** + * {@inheritDoc} + * + *

This implementation disregards the endpoint being connected to as well as the authenticator + * class sent by the server, and always returns the same credentials. + */ + @NonNull + @Override + protected Credentials getCredentials( + @NonNull EndPoint endPoint, @NonNull String serverAuthenticator) { + return new Credentials(username.clone(), password.clone(), authorizationId.clone()); + } +} diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/auth/SyncAuthenticator.java b/core/src/main/java/com/datastax/oss/driver/api/core/auth/SyncAuthenticator.java index d2d1d5d5f3b..016ac25680b 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/auth/SyncAuthenticator.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/auth/SyncAuthenticator.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -35,7 +37,11 @@ public interface SyncAuthenticator extends Authenticator { *

{@link #initialResponse()} calls this and wraps the result in an immediately completed * future. * - * @return The initial response to send to the server (which may be {@code null}). + * @return The initial response to send to the server (which may be {@code null}). Note that, if + * the returned byte buffer is writable, the driver will clear its contents immediately + * after use (to avoid keeping sensitive information in memory); do not reuse the same buffer + * across multiple invocations. Alternatively, if the contents are not sensitive, you can make + * the buffer {@linkplain ByteBuffer#asReadOnlyBuffer() read-only} and safely reuse it. */ @Nullable ByteBuffer initialResponseSync(); @@ -48,7 +54,11 @@ public interface SyncAuthenticator extends Authenticator { * * @param challenge the server's SASL challenge; may be {@code null}. * @return The updated SASL token (which may be {@code null} to indicate the client requires no - * further action). + * further action). Note that, if the returned byte buffer is writable, the driver will + * clear its contents immediately after use (to avoid keeping sensitive information in + * memory); do not reuse the same buffer across multiple invocations. Alternatively, if the + * contents are not sensitive, you can make the buffer {@linkplain + * ByteBuffer#asReadOnlyBuffer() read-only} and safely reuse it. */ @Nullable ByteBuffer evaluateChallengeSync(@Nullable ByteBuffer challenge); diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/auth/package-info.java b/core/src/main/java/com/datastax/oss/driver/api/core/auth/package-info.java index d5d4efd9c9d..b265b9ba463 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/auth/package-info.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/auth/package-info.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/config/DefaultDriverOption.java b/core/src/main/java/com/datastax/oss/driver/api/core/config/DefaultDriverOption.java index 89d8365de78..2900e897cce 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/config/DefaultDriverOption.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/config/DefaultDriverOption.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -23,157 +25,1023 @@ *

Refer to {@code reference.conf} in the driver codebase for a full description of each option. */ public enum DefaultDriverOption implements DriverOption { + /** + * The contact points to use for the initial connection to the cluster. + * + *

Value type: {@link java.util.List List}<{@link String}> + */ CONTACT_POINTS("basic.contact-points"), + /** + * A name that uniquely identifies the driver instance. + * + *

Value-type: {@link String} + */ SESSION_NAME("basic.session-name"), + /** + * The name of the keyspace that the session should initially be connected to. + * + *

Value-type: {@link String} + */ SESSION_KEYSPACE("basic.session-keyspace"), + /** + * How often the driver tries to reload the configuration. + * + *

Value-type: {@link java.time.Duration Duration} + */ CONFIG_RELOAD_INTERVAL("basic.config-reload-interval"), + /** + * How long the driver waits for a request to complete. + * + *

Value-type: {@link java.time.Duration Duration} + */ REQUEST_TIMEOUT("basic.request.timeout"), + /** + * The consistency level. + * + *

Value-Type: {@link String} + */ REQUEST_CONSISTENCY("basic.request.consistency"), + /** + * The page size. + * + *

Value-Type: int + */ REQUEST_PAGE_SIZE("basic.request.page-size"), + /** + * The serial consistency level. + * + *

Value-type: {@link String} + */ REQUEST_SERIAL_CONSISTENCY("basic.request.serial-consistency"), + /** + * The default idempotence of a request. + * + *

Value-type: boolean + */ REQUEST_DEFAULT_IDEMPOTENCE("basic.request.default-idempotence"), + // LOAD_BALANCING_POLICY is a collection of sub-properties LOAD_BALANCING_POLICY("basic.load-balancing-policy"), + /** + * The class of the load balancing policy. + * + *

Value-type: {@link String} + */ LOAD_BALANCING_POLICY_CLASS("basic.load-balancing-policy.class"), + /** + * The datacenter that is considered "local". + * + *

Value-type: {@link String} + */ LOAD_BALANCING_LOCAL_DATACENTER("basic.load-balancing-policy.local-datacenter"), + /** + * A custom filter to include/exclude nodes. + * + *

Value-Type: {@link String} + * + * @deprecated use {@link #LOAD_BALANCING_DISTANCE_EVALUATOR_CLASS} instead. + */ + @Deprecated LOAD_BALANCING_FILTER_CLASS("basic.load-balancing-policy.filter.class"), + /** + * The timeout to use for internal queries that run as part of the initialization process + * + *

Value-type: {@link java.time.Duration Duration} + */ CONNECTION_INIT_QUERY_TIMEOUT("advanced.connection.init-query-timeout"), + /** + * The timeout to use when the driver changes the keyspace on a connection at runtime. + * + *

Value-type: {@link java.time.Duration Duration} + */ CONNECTION_SET_KEYSPACE_TIMEOUT("advanced.connection.set-keyspace-timeout"), + /** + * The maximum number of requests that can be executed concurrently on a connection + * + *

Value-type: int + */ CONNECTION_MAX_REQUESTS("advanced.connection.max-requests-per-connection"), + /** + * The maximum number of "orphaned" requests before a connection gets closed automatically. + * + *

Value-type: int + */ CONNECTION_MAX_ORPHAN_REQUESTS("advanced.connection.max-orphan-requests"), + /** + * Whether to log non-fatal errors when the driver tries to open a new connection. + * + *

Value-type: boolean + */ CONNECTION_WARN_INIT_ERROR("advanced.connection.warn-on-init-error"), + /** + * The number of connections in the LOCAL pool. + * + *

Value-type: int + */ CONNECTION_POOL_LOCAL_SIZE("advanced.connection.pool.local.size"), + /** + * The number of connections in the REMOTE pool. + * + *

Value-type: int + */ CONNECTION_POOL_REMOTE_SIZE("advanced.connection.pool.remote.size"), + /** + * Whether to schedule reconnection attempts if all contact points are unreachable on the first + * initialization attempt. + * + *

Value-type: boolean + */ RECONNECT_ON_INIT("advanced.reconnect-on-init"), + /** + * The class of the reconnection policy. + * + *

Value-type: {@link String} + */ RECONNECTION_POLICY_CLASS("advanced.reconnection-policy.class"), + /** + * Base delay for computing time between reconnection attempts. + * + *

Value-type: {@link java.time.Duration Duration} + */ RECONNECTION_BASE_DELAY("advanced.reconnection-policy.base-delay"), + /** + * Maximum delay between reconnection attempts. + * + *

Value-type: {@link java.time.Duration Duration} + */ RECONNECTION_MAX_DELAY("advanced.reconnection-policy.max-delay"), + // RETRY_POLICY is a collection of sub-properties RETRY_POLICY("advanced.retry-policy"), + /** + * The class of the retry policy. + * + *

Value-type: {@link String} + */ RETRY_POLICY_CLASS("advanced.retry-policy.class"), + // SPECULATIVE_EXECUTION_POLICY is a collection of sub-properties SPECULATIVE_EXECUTION_POLICY("advanced.speculative-execution-policy"), + /** + * The class of the speculative execution policy. + * + *

Value-type: {@link String} + */ SPECULATIVE_EXECUTION_POLICY_CLASS("advanced.speculative-execution-policy.class"), + /** + * The maximum number of executions. + * + *

Value-type: int + */ SPECULATIVE_EXECUTION_MAX("advanced.speculative-execution-policy.max-executions"), + /** + * The delay between each execution. + * + *

Value-type: {@link java.time.Duration Duration} + */ SPECULATIVE_EXECUTION_DELAY("advanced.speculative-execution-policy.delay"), + /** + * The class of the authentication provider. + * + *

Value-type: {@link String} + */ AUTH_PROVIDER_CLASS("advanced.auth-provider.class"), + /** + * Plain text auth provider username. + * + *

Value-type: {@link String} + */ AUTH_PROVIDER_USER_NAME("advanced.auth-provider.username"), + /** + * Plain text auth provider password. + * + *

Value-type: {@link String} + */ AUTH_PROVIDER_PASSWORD("advanced.auth-provider.password"), + /** + * The class of the SSL Engine Factory. + * + *

Value-type: {@link String} + */ SSL_ENGINE_FACTORY_CLASS("advanced.ssl-engine-factory.class"), + /** + * The cipher suites to enable when creating an SSLEngine for a connection. + * + *

Value type: {@link java.util.List List}<{@link String}> + */ SSL_CIPHER_SUITES("advanced.ssl-engine-factory.cipher-suites"), + /** + * Whether or not to require validation that the hostname of the server certificate's common name + * matches the hostname of the server being connected to. + * + *

Value-type: boolean + */ SSL_HOSTNAME_VALIDATION("advanced.ssl-engine-factory.hostname-validation"), + /** + * The location of the keystore file. + * + *

Value-type: {@link String} + */ SSL_KEYSTORE_PATH("advanced.ssl-engine-factory.keystore-path"), + /** + * The keystore password. + * + *

Value-type: {@link String} + */ SSL_KEYSTORE_PASSWORD("advanced.ssl-engine-factory.keystore-password"), + /** + * The location of the truststore file. + * + *

Value-type: {@link String} + */ SSL_TRUSTSTORE_PATH("advanced.ssl-engine-factory.truststore-path"), + /** + * The truststore password. + * + *

Value-type: {@link String} + */ SSL_TRUSTSTORE_PASSWORD("advanced.ssl-engine-factory.truststore-password"), + /** + * The class of the generator that assigns a microsecond timestamp to each request. + * + *

Value-type: {@link String} + */ TIMESTAMP_GENERATOR_CLASS("advanced.timestamp-generator.class"), + /** + * Whether to force the driver to use Java's millisecond-precision system clock. + * + *

Value-type: boolean + */ TIMESTAMP_GENERATOR_FORCE_JAVA_CLOCK("advanced.timestamp-generator.force-java-clock"), + /** + * How far in the future timestamps are allowed to drift before the warning is logged. + * + *

Value-type: {@link java.time.Duration Duration} + */ TIMESTAMP_GENERATOR_DRIFT_WARNING_THRESHOLD( "advanced.timestamp-generator.drift-warning.threshold"), + /** + * How often the warning will be logged if timestamps keep drifting above the threshold. + * + *

Value-type: {@link java.time.Duration Duration} + */ TIMESTAMP_GENERATOR_DRIFT_WARNING_INTERVAL("advanced.timestamp-generator.drift-warning.interval"), + /** + * The class of a session-wide component that tracks the outcome of requests. + * + *

Value-type: {@link String} + * + * @deprecated Use {@link #REQUEST_TRACKER_CLASSES} instead. + */ + @Deprecated REQUEST_TRACKER_CLASS("advanced.request-tracker.class"), + /** + * Whether to log successful requests. + * + *

Value-type: boolean + */ REQUEST_LOGGER_SUCCESS_ENABLED("advanced.request-tracker.logs.success.enabled"), + /** + * The threshold to classify a successful request as "slow". + * + *

Value-type: {@link java.time.Duration Duration} + */ REQUEST_LOGGER_SLOW_THRESHOLD("advanced.request-tracker.logs.slow.threshold"), + /** + * Whether to log slow requests. + * + *

Value-type: boolean + */ REQUEST_LOGGER_SLOW_ENABLED("advanced.request-tracker.logs.slow.enabled"), + /** + * Whether to log failed requests. + * + *

Value-type: boolean + */ REQUEST_LOGGER_ERROR_ENABLED("advanced.request-tracker.logs.error.enabled"), + /** + * The maximum length of the query string in the log message. + * + *

Value-type: int + */ REQUEST_LOGGER_MAX_QUERY_LENGTH("advanced.request-tracker.logs.max-query-length"), + /** + * Whether to log bound values in addition to the query string. + * + *

Value-type: boolean + */ REQUEST_LOGGER_VALUES("advanced.request-tracker.logs.show-values"), + /** + * The maximum length for bound values in the log message. + * + *

Value-type: int + */ REQUEST_LOGGER_MAX_VALUE_LENGTH("advanced.request-tracker.logs.max-value-length"), + /** + * The maximum number of bound values to log. + * + *

Value-type: int + */ REQUEST_LOGGER_MAX_VALUES("advanced.request-tracker.logs.max-values"), + /** + * Whether to log stack traces for failed queries. + * + *

Value-type: boolean + */ REQUEST_LOGGER_STACK_TRACES("advanced.request-tracker.logs.show-stack-traces"), + /** + * The class of a session-wide component that controls the rate at which requests are executed. + * + *

Value-type: {@link String} + */ REQUEST_THROTTLER_CLASS("advanced.throttler.class"), + /** + * The maximum number of requests that are allowed to execute in parallel. + * + *

Value-type: int + */ REQUEST_THROTTLER_MAX_CONCURRENT_REQUESTS("advanced.throttler.max-concurrent-requests"), + /** + * The maximum allowed request rate. + * + *

Value-type: int + */ REQUEST_THROTTLER_MAX_REQUESTS_PER_SECOND("advanced.throttler.max-requests-per-second"), + /** + * The maximum number of requests that can be enqueued when the throttling threshold is exceeded. + * + *

Value-type: int + */ REQUEST_THROTTLER_MAX_QUEUE_SIZE("advanced.throttler.max-queue-size"), + /** + * How often the throttler attempts to dequeue requests. + * + *

Value-type: {@link java.time.Duration Duration} + */ REQUEST_THROTTLER_DRAIN_INTERVAL("advanced.throttler.drain-interval"), + /** + * The class of a session-wide component that listens for node state changes. + * + *

Value-type: {@link String} + * + * @deprecated Use {@link #METADATA_NODE_STATE_LISTENER_CLASSES} instead. + */ + @Deprecated METADATA_NODE_STATE_LISTENER_CLASS("advanced.node-state-listener.class"), + /** + * The class of a session-wide component that listens for schema changes. + * + *

Value-type: {@link String} + * + * @deprecated Use {@link #METADATA_SCHEMA_CHANGE_LISTENER_CLASSES} instead. + */ + @Deprecated METADATA_SCHEMA_CHANGE_LISTENER_CLASS("advanced.schema-change-listener.class"), + /** + * The class of the address translator to use to convert the addresses sent by Cassandra nodes + * into ones that the driver uses to connect. + * + *

Value-type: {@link String} + */ ADDRESS_TRANSLATOR_CLASS("advanced.address-translator.class"), + /** + * The native protocol version to use. + * + *

Value-type: {@link String} + */ PROTOCOL_VERSION("advanced.protocol.version"), + /** + * The name of the algorithm used to compress protocol frames. + * + *

Value-type: {@link String} + */ PROTOCOL_COMPRESSION("advanced.protocol.compression"), + /** + * The maximum length, in bytes, of the frames supported by the driver. + * + *

Value-type: long + */ PROTOCOL_MAX_FRAME_LENGTH("advanced.protocol.max-frame-length"), + /** + * Whether a warning is logged when a request (such as a CQL `USE ...`) changes the active + * keyspace. + * + *

Value-type: boolean + */ REQUEST_WARN_IF_SET_KEYSPACE("advanced.request.warn-if-set-keyspace"), + /** + * How many times the driver will attempt to fetch the query trace if it is not ready yet. + * + *

Value-type: int + */ REQUEST_TRACE_ATTEMPTS("advanced.request.trace.attempts"), + /** + * The interval between each attempt. + * + *

Value-type: {@link java.time.Duration Duration} + */ REQUEST_TRACE_INTERVAL("advanced.request.trace.interval"), + /** + * The consistency level to use for trace queries. + * + *

Value-type: {@link String} + */ REQUEST_TRACE_CONSISTENCY("advanced.request.trace.consistency"), + /** + * List of enabled session-level metrics. + * + *

Value type: {@link java.util.List List}<{@link String}> + */ METRICS_SESSION_ENABLED("advanced.metrics.session.enabled"), + /** + * List of enabled node-level metrics. + * + *

Value type: {@link java.util.List List}<{@link String}> + */ METRICS_NODE_ENABLED("advanced.metrics.node.enabled"), + /** + * The largest latency that we expect to record for requests. + * + *

Value-type: {@link java.time.Duration Duration} + */ METRICS_SESSION_CQL_REQUESTS_HIGHEST("advanced.metrics.session.cql-requests.highest-latency"), + /** + * The number of significant decimal digits to which internal structures will maintain for + * requests. + * + *

Value-type: int + */ METRICS_SESSION_CQL_REQUESTS_DIGITS("advanced.metrics.session.cql-requests.significant-digits"), + /** + * The interval at which percentile data is refreshed for requests. + * + *

Value-type: {@link java.time.Duration Duration} + */ METRICS_SESSION_CQL_REQUESTS_INTERVAL("advanced.metrics.session.cql-requests.refresh-interval"), + /** + * The largest latency that we expect to record for throttling. + * + *

Value-type: {@link java.time.Duration Duration} + */ METRICS_SESSION_THROTTLING_HIGHEST("advanced.metrics.session.throttling.delay.highest-latency"), + /** + * The number of significant decimal digits to which internal structures will maintain for + * throttling. + * + *

Value-type: int + */ METRICS_SESSION_THROTTLING_DIGITS("advanced.metrics.session.throttling.delay.significant-digits"), + /** + * The interval at which percentile data is refreshed for throttling. + * + *

Value-type: {@link java.time.Duration Duration} + */ METRICS_SESSION_THROTTLING_INTERVAL("advanced.metrics.session.throttling.delay.refresh-interval"), + /** + * The largest latency that we expect to record for requests. + * + *

Value-type: {@link java.time.Duration Duration} + */ METRICS_NODE_CQL_MESSAGES_HIGHEST("advanced.metrics.node.cql-messages.highest-latency"), + /** + * The number of significant decimal digits to which internal structures will maintain for + * requests. + * + *

Value-type: int + */ METRICS_NODE_CQL_MESSAGES_DIGITS("advanced.metrics.node.cql-messages.significant-digits"), + /** + * The interval at which percentile data is refreshed for requests. + * + *

Value-type: {@link java.time.Duration Duration} + */ METRICS_NODE_CQL_MESSAGES_INTERVAL("advanced.metrics.node.cql-messages.refresh-interval"), + /** + * Whether or not to disable the Nagle algorithm. + * + *

Value-type: boolean + */ SOCKET_TCP_NODELAY("advanced.socket.tcp-no-delay"), + /** + * Whether or not to enable TCP keep-alive probes. + * + *

Value-type: boolean + */ SOCKET_KEEP_ALIVE("advanced.socket.keep-alive"), + /** + * Whether or not to allow address reuse. + * + *

Value-type: boolean + */ SOCKET_REUSE_ADDRESS("advanced.socket.reuse-address"), + /** + * Sets the linger interval. + * + *

Value-type: int + */ SOCKET_LINGER_INTERVAL("advanced.socket.linger-interval"), + /** + * Sets a hint to the size of the underlying buffers for incoming network I/O. + * + *

Value-type: int + */ SOCKET_RECEIVE_BUFFER_SIZE("advanced.socket.receive-buffer-size"), + /** + * Sets a hint to the size of the underlying buffers for outgoing network I/O. + * + *

Value-type: int + */ SOCKET_SEND_BUFFER_SIZE("advanced.socket.send-buffer-size"), + /** + * The connection heartbeat interval. + * + *

Value-type: {@link java.time.Duration Duration} + */ HEARTBEAT_INTERVAL("advanced.heartbeat.interval"), + /** + * How long the driver waits for the response to a heartbeat. + * + *

Value-type: {@link java.time.Duration Duration} + */ HEARTBEAT_TIMEOUT("advanced.heartbeat.timeout"), + /** + * How long the driver waits to propagate a Topology event. + * + *

Value-type: {@link java.time.Duration Duration} + */ METADATA_TOPOLOGY_WINDOW("advanced.metadata.topology-event-debouncer.window"), + /** + * The maximum number of events that can accumulate. + * + *

Value-type: int + */ METADATA_TOPOLOGY_MAX_EVENTS("advanced.metadata.topology-event-debouncer.max-events"), + /** + * Whether schema metadata is enabled. + * + *

Value-type: boolean + */ METADATA_SCHEMA_ENABLED("advanced.metadata.schema.enabled"), + /** + * The timeout for the requests to the schema tables. + * + *

Value-type: {@link java.time.Duration Duration} + */ METADATA_SCHEMA_REQUEST_TIMEOUT("advanced.metadata.schema.request-timeout"), + /** + * The page size for the requests to the schema tables. + * + *

Value-type: int + */ METADATA_SCHEMA_REQUEST_PAGE_SIZE("advanced.metadata.schema.request-page-size"), + /** + * The list of keyspaces for which schema and token metadata should be maintained. + * + *

Value type: {@link java.util.List List}<{@link String}> + */ METADATA_SCHEMA_REFRESHED_KEYSPACES("advanced.metadata.schema.refreshed-keyspaces"), + /** + * How long the driver waits to apply a refresh. + * + *

Value-type: {@link java.time.Duration Duration} + */ METADATA_SCHEMA_WINDOW("advanced.metadata.schema.debouncer.window"), + /** + * The maximum number of refreshes that can accumulate. + * + *

Value-type: int + */ METADATA_SCHEMA_MAX_EVENTS("advanced.metadata.schema.debouncer.max-events"), + /** + * Whether token metadata is enabled. + * + *

Value-type: boolean + */ METADATA_TOKEN_MAP_ENABLED("advanced.metadata.token-map.enabled"), + /** + * How long the driver waits for responses to control queries. + * + *

Value-type: {@link java.time.Duration Duration} + */ CONTROL_CONNECTION_TIMEOUT("advanced.control-connection.timeout"), + /** + * The interval between each schema agreement check attempt. + * + *

Value-type: {@link java.time.Duration Duration} + */ CONTROL_CONNECTION_AGREEMENT_INTERVAL("advanced.control-connection.schema-agreement.interval"), + /** + * The timeout after which schema agreement fails. + * + *

Value-type: {@link java.time.Duration Duration} + */ CONTROL_CONNECTION_AGREEMENT_TIMEOUT("advanced.control-connection.schema-agreement.timeout"), + /** + * Whether to log a warning if schema agreement fails. + * + *

Value-type: boolean + */ CONTROL_CONNECTION_AGREEMENT_WARN("advanced.control-connection.schema-agreement.warn-on-failure"), + /** + * Whether `Session.prepare` calls should be sent to all nodes in the cluster. + * + *

Value-type: boolean + */ PREPARE_ON_ALL_NODES("advanced.prepared-statements.prepare-on-all-nodes"), + /** + * Whether the driver tries to prepare on new nodes at all. + * + *

Value-type: boolean + */ REPREPARE_ENABLED("advanced.prepared-statements.reprepare-on-up.enabled"), + /** + * Whether to check `system.prepared_statements` on the target node before repreparing. + * + *

Value-type: boolean + */ REPREPARE_CHECK_SYSTEM_TABLE("advanced.prepared-statements.reprepare-on-up.check-system-table"), + /** + * The maximum number of statements that should be reprepared. + * + *

Value-type: int + */ REPREPARE_MAX_STATEMENTS("advanced.prepared-statements.reprepare-on-up.max-statements"), + /** + * The maximum number of concurrent requests when repreparing. + * + *

Value-type: int + */ REPREPARE_MAX_PARALLELISM("advanced.prepared-statements.reprepare-on-up.max-parallelism"), + /** + * The request timeout when repreparing. + * + *

Value-type: {@link java.time.Duration Duration} + */ REPREPARE_TIMEOUT("advanced.prepared-statements.reprepare-on-up.timeout"), + /** + * The number of threads in the I/O group. + * + *

Value-type: int + */ NETTY_IO_SIZE("advanced.netty.io-group.size"), + /** + * Quiet period for I/O group shutdown. + * + *

Value-type: int + */ NETTY_IO_SHUTDOWN_QUIET_PERIOD("advanced.netty.io-group.shutdown.quiet-period"), + /** + * Max time to wait for I/O group shutdown. + * + *

Value-type: int + */ NETTY_IO_SHUTDOWN_TIMEOUT("advanced.netty.io-group.shutdown.timeout"), + /** + * Units for I/O group quiet period and timeout. + * + *

Value-type: {@link String} + */ NETTY_IO_SHUTDOWN_UNIT("advanced.netty.io-group.shutdown.unit"), + /** + * The number of threads in the Admin group. + * + *

Value-type: int + */ NETTY_ADMIN_SIZE("advanced.netty.admin-group.size"), + /** + * Quiet period for admin group shutdown. + * + *

Value-type: int + */ NETTY_ADMIN_SHUTDOWN_QUIET_PERIOD("advanced.netty.admin-group.shutdown.quiet-period"), + /** + * Max time to wait for admin group shutdown. + * + *

Value-type: {@link String} + */ NETTY_ADMIN_SHUTDOWN_TIMEOUT("advanced.netty.admin-group.shutdown.timeout"), + /** + * Units for admin group quite period and timeout. + * + *

Value-type: {@link String} + */ NETTY_ADMIN_SHUTDOWN_UNIT("advanced.netty.admin-group.shutdown.unit"), + /** @deprecated This option was removed in version 4.6.1. */ + @Deprecated COALESCER_MAX_RUNS("advanced.coalescer.max-runs-with-no-work"), + /** + * The coalescer reschedule interval. + * + *

Value-type: {@link java.time.Duration Duration} + */ COALESCER_INTERVAL("advanced.coalescer.reschedule-interval"), + /** + * Whether to resolve the addresses passed to `basic.contact-points`. + * + *

Value-type: boolean + */ RESOLVE_CONTACT_POINTS("advanced.resolve-contact-points"), + /** + * This is how frequent the timer should wake up to check for timed-out tasks or speculative + * executions. + * + *

Value-type: {@link java.time.Duration Duration} + */ NETTY_TIMER_TICK_DURATION("advanced.netty.timer.tick-duration"), + /** + * Number of ticks in the Timer wheel. + * + *

Value-type: int + */ NETTY_TIMER_TICKS_PER_WHEEL("advanced.netty.timer.ticks-per-wheel"), + /** + * Whether logging of server warnings generated during query execution should be disabled by the + * driver. + * + *

Value-type: boolean + */ REQUEST_LOG_WARNINGS("advanced.request.log-warnings"), - ; + + /** + * Whether the threads created by the driver should be daemon threads. + * + *

Value-type: boolean + */ + NETTY_DAEMON("advanced.netty.daemon"), + + /** + * The location of the cloud secure bundle used to connect to DataStax Apache Cassandra as a + * service. + * + *

Value-type: {@link String} + */ + CLOUD_SECURE_CONNECT_BUNDLE("basic.cloud.secure-connect-bundle"), + + /** + * Whether the slow replica avoidance should be enabled in the default LBP. + * + *

Value-type: boolean + */ + LOAD_BALANCING_POLICY_SLOW_AVOIDANCE("basic.load-balancing-policy.slow-replica-avoidance"), + + /** + * The timeout to use when establishing driver connections. + * + *

Value-type: {@link java.time.Duration Duration} + */ + CONNECTION_CONNECT_TIMEOUT("advanced.connection.connect-timeout"), + + /** + * The maximum number of live sessions that are allowed to coexist in a given VM. + * + *

Value-type: int + */ + SESSION_LEAK_THRESHOLD("advanced.session-leak.threshold"), + /** + * The period of inactivity after which the node level metrics will be evicted. The eviction will + * happen only if none of the enabled node-level metrics is updated for a given node within this + * time window. + * + *

Value-type: {@link java.time.Duration Duration} + */ + METRICS_NODE_EXPIRE_AFTER("advanced.metrics.node.expire-after"), + + /** + * The classname of the desired MetricsFactory implementation. + * + *

Value-type: {@link String} + */ + METRICS_FACTORY_CLASS("advanced.metrics.factory.class"), + + /** + * The maximum number of nodes from remote DCs to include in query plans. + * + *

Value-Type: int + */ + LOAD_BALANCING_DC_FAILOVER_MAX_NODES_PER_REMOTE_DC( + "advanced.load-balancing-policy.dc-failover.max-nodes-per-remote-dc"), + /** + * Whether to consider nodes from remote DCs if the request's consistency level is local. + * + *

Value-Type: boolean + */ + LOAD_BALANCING_DC_FAILOVER_ALLOW_FOR_LOCAL_CONSISTENCY_LEVELS( + "advanced.load-balancing-policy.dc-failover.allow-for-local-consistency-levels"), + + /** + * The classname of the desired {@code MetricIdGenerator} implementation. + * + *

Value-type: {@link String} + */ + METRICS_ID_GENERATOR_CLASS("advanced.metrics.id-generator.class"), + + /** + * The value of the prefix to prepend to all metric names. + * + *

Value-type: {@link String} + */ + METRICS_ID_GENERATOR_PREFIX("advanced.metrics.id-generator.prefix"), + + /** + * The class name of a custom {@link + * com.datastax.oss.driver.api.core.loadbalancing.NodeDistanceEvaluator}. + * + *

Value-Type: {@link String} + */ + LOAD_BALANCING_DISTANCE_EVALUATOR_CLASS("basic.load-balancing-policy.evaluator.class"), + + /** + * The shortest latency that we expect to record for requests. + * + *

Value-type: {@link java.time.Duration Duration} + */ + METRICS_SESSION_CQL_REQUESTS_LOWEST("advanced.metrics.session.cql-requests.lowest-latency"), + /** + * Optional service-level objectives to meet, as a list of latencies to track. + * + *

Value-type: List of {@link java.time.Duration Duration} + */ + METRICS_SESSION_CQL_REQUESTS_SLO("advanced.metrics.session.cql-requests.slo"), + + /** + * The shortest latency that we expect to record for throttling. + * + *

Value-type: {@link java.time.Duration Duration} + */ + METRICS_SESSION_THROTTLING_LOWEST("advanced.metrics.session.throttling.delay.lowest-latency"), + /** + * Optional service-level objectives to meet, as a list of latencies to track. + * + *

Value-type: List of {@link java.time.Duration Duration} + */ + METRICS_SESSION_THROTTLING_SLO("advanced.metrics.session.throttling.delay.slo"), + + /** + * The shortest latency that we expect to record for requests. + * + *

Value-type: {@link java.time.Duration Duration} + */ + METRICS_NODE_CQL_MESSAGES_LOWEST("advanced.metrics.node.cql-messages.lowest-latency"), + /** + * Optional service-level objectives to meet, as a list of latencies to track. + * + *

Value-type: List of {@link java.time.Duration Duration} + */ + METRICS_NODE_CQL_MESSAGES_SLO("advanced.metrics.node.cql-messages.slo"), + + /** + * Whether the prepared statements cache use weak values. + * + *

Value-type: boolean + */ + PREPARED_CACHE_WEAK_VALUES("advanced.prepared-statements.prepared-cache.weak-values"), + + /** + * The classes of session-wide components that track the outcome of requests. + * + *

Value-type: List of {@link String} + */ + REQUEST_TRACKER_CLASSES("advanced.request-tracker.classes"), + + /** + * The classes of session-wide components that listen for node state changes. + * + *

Value-type: List of {@link String} + */ + METADATA_NODE_STATE_LISTENER_CLASSES("advanced.node-state-listener.classes"), + + /** + * The classes of session-wide components that listen for schema changes. + * + *

Value-type: List of {@link String} + */ + METADATA_SCHEMA_CHANGE_LISTENER_CLASSES("advanced.schema-change-listener.classes"), + /** + * Optional list of percentiles to publish for cql-requests metric. Produces an additional time + * series for each requested percentile. This percentile is computed locally, and so can't be + * aggregated with percentiles computed across other dimensions (e.g. in a different instance). + * + *

Value type: {@link java.util.List List}<{@link Double}> + */ + METRICS_SESSION_CQL_REQUESTS_PUBLISH_PERCENTILES( + "advanced.metrics.session.cql-requests.publish-percentiles"), + /** + * Optional list of percentiles to publish for node cql-messages metric. Produces an additional + * time series for each requested percentile. This percentile is computed locally, and so can't be + * aggregated with percentiles computed across other dimensions (e.g. in a different instance). + * + *

Value type: {@link java.util.List List}<{@link Double}> + */ + METRICS_NODE_CQL_MESSAGES_PUBLISH_PERCENTILES( + "advanced.metrics.node.cql-messages.publish-percentiles"), + /** + * Optional list of percentiles to publish for throttling delay metric.Produces an additional time + * series for each requested percentile. This percentile is computed locally, and so can't be + * aggregated with percentiles computed across other dimensions (e.g. in a different instance). + * + *

Value type: {@link java.util.List List}<{@link Double}> + */ + METRICS_SESSION_THROTTLING_PUBLISH_PERCENTILES( + "advanced.metrics.session.throttling.delay.publish-percentiles"), + /** + * Adds histogram buckets used to generate aggregable percentile approximations in monitoring + * systems that have query facilities to do so (e.g. Prometheus histogram_quantile, Atlas + * percentiles). + * + *

Value-type: boolean + */ + METRICS_GENERATE_AGGREGABLE_HISTOGRAMS("advanced.metrics.histograms.generate-aggregable"), + /** + * The duration between attempts to reload the keystore. + * + *

Value-type: {@link java.time.Duration} + */ + SSL_KEYSTORE_RELOAD_INTERVAL("advanced.ssl-engine-factory.keystore-reload-interval"), + /** + * Ordered preference list of remote dcs optionally supplied for automatic failover. + * + *

Value type: {@link java.util.List List}<{@link String}> + */ + LOAD_BALANCING_DC_FAILOVER_PREFERRED_REMOTE_DCS( + "advanced.load-balancing-policy.dc-failover.preferred-remote-dcs"), + /** + * Whether or not to do a DNS reverse-lookup of provided server addresses for SAN addresses. + * + *

Value-type: boolean + */ + SSL_ALLOW_DNS_REVERSE_LOOKUP_SAN("advanced.ssl-engine-factory.allow-dns-reverse-lookup-san"), + /** + * The class of session-wide component that generates request IDs. + * + *

Value-type: {@link String} + */ + REQUEST_ID_GENERATOR_CLASS("advanced.request-id.generator.class"), + /** + * An address to always translate all node addresses to that same proxy hostname no matter what IP + * address a node has, but still using its native transport port. + * + *

Value-Type: {@link String} + */ + ADDRESS_TRANSLATOR_ADVERTISED_HOSTNAME("advanced.address-translator.advertised-hostname"), + /** + * A map of Cassandra node subnets (CIDR notations) to target addresses, for example (note quoted + * keys): + * + *

+   * advanced.address-translator.subnet-addresses {
+   *   "100.64.0.0/15" = "cassandra.datacenter1.com:9042"
+   *   "100.66.0.0/15" = "cassandra.datacenter2.com:9042"
+   *   # IPv6 example:
+   *   # "::ffff:6440:0/111" = "cassandra.datacenter1.com:9042"
+   *   # "::ffff:6442:0/111" = "cassandra.datacenter2.com:9042"
+   * }
+   * 
+ * + * Note: subnets must be represented as prefix blocks, see inet.ipaddr.Address.isPrefixBlock() + * + *

Value type: {@link java.util.Map Map}<{@link String},{@link String}> + */ + ADDRESS_TRANSLATOR_SUBNET_ADDRESSES("advanced.address-translator.subnet-addresses"), + /** + * A default address to fallback to if Cassandra node IP isn't contained in any of the configured + * subnets. + * + *

Value-Type: {@link String} + */ + ADDRESS_TRANSLATOR_DEFAULT_ADDRESS("advanced.address-translator.default-address"), + /** + * Whether to resolve the addresses on initialization (if true) or on each node (re-)connection + * (if false). Defaults to false. + * + *

Value-Type: boolean + */ + ADDRESS_TRANSLATOR_RESOLVE_ADDRESSES("advanced.address-translator.resolve-addresses"); private final String path; diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/config/DriverConfig.java b/core/src/main/java/com/datastax/oss/driver/api/core/config/DriverConfig.java index fae096123c2..88519c82a22 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/config/DriverConfig.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/config/DriverConfig.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/config/DriverConfigLoader.java b/core/src/main/java/com/datastax/oss/driver/api/core/config/DriverConfigLoader.java index 04c2b156f1b..15fae232d17 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/config/DriverConfigLoader.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/config/DriverConfigLoader.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -17,13 +19,14 @@ import com.datastax.oss.driver.api.core.context.DriverContext; import com.datastax.oss.driver.api.core.session.SessionBuilder; +import com.datastax.oss.driver.internal.core.config.composite.CompositeDriverConfigLoader; +import com.datastax.oss.driver.internal.core.config.map.MapBasedDriverConfigLoader; import com.datastax.oss.driver.internal.core.config.typesafe.DefaultDriverConfigLoader; import com.datastax.oss.driver.internal.core.config.typesafe.DefaultProgrammaticDriverConfigLoaderBuilder; -import com.typesafe.config.Config; -import com.typesafe.config.ConfigFactory; import edu.umd.cs.findbugs.annotations.NonNull; import java.io.File; import java.net.URL; +import java.nio.file.Path; import java.util.concurrent.CompletionStage; /** @@ -33,10 +36,29 @@ */ public interface DriverConfigLoader extends AutoCloseable { + /** + * Builds an instance using the driver's default implementation (based on Typesafe config) except + * that application-specific classpath resources will be located using the provided {@link + * ClassLoader} instead of {@linkplain Thread#getContextClassLoader() the current thread's context + * class loader}. + * + *

The returned loader will honor the reload interval defined by the option {@code + * basic.config-reload-interval}. + */ + @NonNull + static DriverConfigLoader fromDefaults(@NonNull ClassLoader appClassLoader) { + return new DefaultDriverConfigLoader(appClassLoader); + } + /** * Builds an instance using the driver's default implementation (based on Typesafe config), except * that application-specific options are loaded from a classpath resource with a custom name. * + *

The class loader used to locate application-specific classpath resources is {@linkplain + * Thread#getContextClassLoader() the current thread's context class loader}. This might not be + * suitable for OSGi deployments, which should use {@link #fromClasspath(String, ClassLoader)} + * instead. + * *

More precisely, configuration properties are loaded and merged from the following * (first-listed are higher priority): * @@ -57,16 +79,43 @@ public interface DriverConfigLoader extends AutoCloseable { */ @NonNull static DriverConfigLoader fromClasspath(@NonNull String resourceBaseName) { - return new DefaultDriverConfigLoader( - () -> { - ConfigFactory.invalidateCaches(); - Config config = - ConfigFactory.defaultOverrides() - .withFallback(ConfigFactory.parseResourcesAnySyntax(resourceBaseName)) - .withFallback(ConfigFactory.defaultReference()) - .resolve(); - return config.getConfig(DefaultDriverConfigLoader.DEFAULT_ROOT_PATH); - }); + return fromClasspath(resourceBaseName, Thread.currentThread().getContextClassLoader()); + } + + /** + * Just like {@link #fromClasspath(java.lang.String)} except that application-specific classpath + * resources will be located using the provided {@link ClassLoader} instead of {@linkplain + * Thread#getContextClassLoader() the current thread's context class loader}. + */ + @NonNull + static DriverConfigLoader fromClasspath( + @NonNull String resourceBaseName, @NonNull ClassLoader appClassLoader) { + return DefaultDriverConfigLoader.fromClasspath(resourceBaseName, appClassLoader); + } + + /** + * Builds an instance using the driver's default implementation (based on Typesafe config), except + * that application-specific options are loaded from the given path. + * + *

More precisely, configuration properties are loaded and merged from the following + * (first-listed are higher priority): + * + *

    + *
  • system properties + *
  • the contents of {@code file} + *
  • {@code reference.conf} (all resources on classpath with this name). In particular, this + * will load the {@code reference.conf} included in the core driver JAR, that defines + * default options for all mandatory options. + *
+ * + * The resulting configuration is expected to contain a {@code datastax-java-driver} section. + * + *

The returned loader will honor the reload interval defined by the option {@code + * basic.config-reload-interval}. + */ + @NonNull + static DriverConfigLoader fromPath(@NonNull Path file) { + return fromFile(file.toFile()); } /** @@ -91,16 +140,7 @@ static DriverConfigLoader fromClasspath(@NonNull String resourceBaseName) { */ @NonNull static DriverConfigLoader fromFile(@NonNull File file) { - return new DefaultDriverConfigLoader( - () -> { - ConfigFactory.invalidateCaches(); - Config config = - ConfigFactory.defaultOverrides() - .withFallback(ConfigFactory.parseFileAnySyntax(file)) - .withFallback(ConfigFactory.defaultReference()) - .resolve(); - return config.getConfig(DefaultDriverConfigLoader.DEFAULT_ROOT_PATH); - }); + return DefaultDriverConfigLoader.fromFile(file); } /** @@ -125,21 +165,45 @@ static DriverConfigLoader fromFile(@NonNull File file) { */ @NonNull static DriverConfigLoader fromUrl(@NonNull URL url) { - return new DefaultDriverConfigLoader( - () -> { - ConfigFactory.invalidateCaches(); - Config config = - ConfigFactory.defaultOverrides() - .withFallback(ConfigFactory.parseURL(url)) - .withFallback(ConfigFactory.defaultReference()) - .resolve(); - return config.getConfig(DefaultDriverConfigLoader.DEFAULT_ROOT_PATH); - }); + return DefaultDriverConfigLoader.fromUrl(url); + } + + /** + * Builds an instance using the driver's default implementation (based on Typesafe config), except + * that application-specific options are parsed from the given string. + * + *

The string must be in HOCON format and contain a {@code datastax-java-driver} section. + * Options must be separated by line breaks: + * + *

+   * DriverConfigLoader.fromString(
+   *         "datastax-java-driver.basic { session-name = my-app\nrequest.timeout = 1 millisecond }")
+   * 
+ * + *

More precisely, configuration properties are loaded and merged from the following + * (first-listed are higher priority): + * + *

    + *
  • system properties + *
  • the config in {@code contents} + *
  • {@code reference.conf} (all resources on classpath with this name). In particular, this + * will load the {@code reference.conf} included in the core driver JAR, that defines + * default options for all mandatory options. + *
+ * + *

This loader does not support runtime reloading. + */ + @NonNull + static DriverConfigLoader fromString(@NonNull String contents) { + return DefaultDriverConfigLoader.fromString(contents); } /** * Starts a builder that allows configuration options to be overridden programmatically. * + *

Note that {@link #fromMap(OptionsMap)} provides an alternative approach for programmatic + * configuration, that might be more convenient if you wish to completely bypass Typesafe config. + * *

For example: * *

{@code
@@ -183,18 +247,96 @@ static DriverConfigLoader fromUrl(@NonNull URL url) {
    * Note that {@code application.*} is entirely optional, you may choose to only rely on the
    * driver's built-in {@code reference.conf} and programmatic overrides.
    *
+   * 

The class loader used to locate application-specific classpath resources is {@linkplain + * Thread#getContextClassLoader() the current thread's context class loader}. This might not be + * suitable for OSGi deployments, which should use {@link #programmaticBuilder(ClassLoader)} + * instead. + * *

The resulting configuration is expected to contain a {@code datastax-java-driver} section. * *

The loader will honor the reload interval defined by the option {@code * basic.config-reload-interval}. * *

Note that the returned builder is not thread-safe. + * + * @see #fromMap(OptionsMap) */ @NonNull static ProgrammaticDriverConfigLoaderBuilder programmaticBuilder() { return new DefaultProgrammaticDriverConfigLoaderBuilder(); } + /** + * Just like {@link #programmaticBuilder()} except that application-specific classpath resources + * will be located using the provided {@link ClassLoader} instead of {@linkplain + * Thread#getContextClassLoader() the current thread's context class loader}. + */ + @NonNull + static ProgrammaticDriverConfigLoaderBuilder programmaticBuilder( + @NonNull ClassLoader appClassLoader) { + return new DefaultProgrammaticDriverConfigLoaderBuilder(appClassLoader); + } + + /** + * Builds an instance backed by an {@link OptionsMap}, which holds all options in memory. + * + *

This is the simplest implementation. It is intended for clients who wish to completely + * bypass Typesafe config, and instead manage the configuration programmatically. A typical + * example is a third-party tool that already has its own configuration file, and doesn't want to + * introduce a separate mechanism for driver options. + * + *

With this loader, the driver's built-in {@code reference.conf} file is ignored, the provided + * {@link OptionsMap} must explicitly provide all mandatory options. Note however that {@link + * OptionsMap#driverDefaults()} allows you to initialize an instance with the same default values + * as {@code reference.conf}. + * + *

+   * // This creates a configuration equivalent to the built-in reference.conf:
+   * OptionsMap map = OptionsMap.driverDefaults();
+   *
+   * // Customize an option:
+   * map.put(TypedDriverOption.REQUEST_TIMEOUT, Duration.ofSeconds(5));
+   *
+   * DriverConfigLoader loader = DriverConfigLoader.fromMap(map);
+   * CqlSession session = CqlSession.builder()
+   *     .withConfigLoader(loader)
+   *     .build();
+   * 
+ * + *

If the {@link OptionsMap} is modified at runtime, this will be reflected immediately in the + * configuration, you don't need to call {@link #reload()}. Note however that, depending on the + * option, the driver might not react to a configuration change immediately, or ever (this is + * documented in {@code reference.conf}). + * + * @since 4.6.0 + */ + @NonNull + static DriverConfigLoader fromMap(@NonNull OptionsMap source) { + return new MapBasedDriverConfigLoader(source, source.asRawMap()); + } + + /** + * Composes two existing config loaders to form a new one. + * + *

When the driver reads an option, the "primary" config will be queried first. If the option + * is missing, then it will be looked up in the "fallback" config. + * + *

All execution profiles will be surfaced in the new config. If a profile is defined both in + * the primary and the fallback config, its options will be merged using the same precedence rules + * as described above. + * + *

The new config is reloadable if at least one of the input configs is. If you invoke {@link + * DriverConfigLoader#reload()} on the new loader, it will reload whatever is reloadable, or fail + * if nothing is. If the input loaders have periodic reloading built-in, each one will reload at + * its own pace, and the changes will be reflected in the new config. + */ + @NonNull + static DriverConfigLoader compose( + @NonNull DriverConfigLoader primaryConfigLoader, + @NonNull DriverConfigLoader fallbackConfigLoader) { + return new CompositeDriverConfigLoader(primaryConfigLoader, fallbackConfigLoader); + } + /** * Loads the first configuration that will be used to initialize the driver. * @@ -211,26 +353,32 @@ static ProgrammaticDriverConfigLoaderBuilder programmaticBuilder() { void onDriverInit(@NonNull DriverContext context); /** - * Triggers an immediate reload attempt. + * Triggers an immediate reload attempt and returns a stage that completes once the attempt is + * finished, with a boolean indicating whether the configuration changed as a result of this + * reload. * - * @return a stage that completes once the attempt is finished, with a boolean indicating whether - * the configuration changed as a result of this reload. If so, it's also guaranteed that - * internal driver components have been notified by that time; note however that some react to - * the notification asynchronously, so they may not have completely applied all resulting - * changes yet. If this loader does not support programmatic reloading — which you can - * check by calling {@link #supportsReloading()} before this method — the returned - * object will fail immediately with an {@link UnsupportedOperationException}. + *

If so, it's also guaranteed that internal driver components have been notified by that time; + * note however that some react to the notification asynchronously, so they may not have + * completely applied all resulting changes yet. + * + *

If this loader does not support programmatic reloading — which you can check by + * calling {@link #supportsReloading()} before this method — the returned stage should fail + * immediately with an {@link UnsupportedOperationException}. The default implementation of this + * interface does support programmatic reloading however, and never returns a failed stage. */ @NonNull CompletionStage reload(); /** * Whether this implementation supports programmatic reloading with the {@link #reload()} method. + * + *

The default implementation of this interface does support programmatic reloading and always + * returns true. */ boolean supportsReloading(); /** - * Called when the cluster closes. This is a good time to release any external resource, for + * Called when the session closes. This is a good time to release any external resource, for * example cancel a scheduled reloading task. */ @Override diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/config/DriverExecutionProfile.java b/core/src/main/java/com/datastax/oss/driver/api/core/config/DriverExecutionProfile.java index 600b2709065..89c28f0f521 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/config/DriverExecutionProfile.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/config/DriverExecutionProfile.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -15,6 +17,8 @@ */ package com.datastax.oss.driver.api.core.config; +import com.datastax.oss.driver.internal.core.config.DerivedExecutionProfile; +import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableMap; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import java.time.Duration; @@ -182,12 +186,28 @@ default List getDurationList( /** * Returns a representation of all the child options under a given option. * - *

This is only used to compare configuration sections across profiles, so the actual - * implementation does not matter, as long as identical sections (same options with same values, - * regardless of order) compare as equal and have the same {@code hashCode()}. + *

This is used by the driver at initialization time, to compare profiles and determine if it + * must create per-profile policies. For example, if two profiles have the same options in the + * {@code basic.load-balancing-policy} section, they will share the same policy instance. But if + * their options differ, two separate instances will be created. + * + *

The runtime return type does not matter, as long as identical sections (same options with + * same values, regardless of order) compare as equal and have the same {@code hashCode()}. The + * default implementation builds a map based on the entries from {@link #entrySet()}, it should be + * good for most cases. */ @NonNull - Object getComparisonKey(@NonNull DriverOption option); + default Object getComparisonKey(@NonNull DriverOption option) { + // This method is only used during driver initialization, performance is not crucial + String prefix = option.getPath(); + ImmutableMap.Builder childOptions = ImmutableMap.builder(); + for (Map.Entry entry : entrySet()) { + if (entry.getKey().startsWith(prefix)) { + childOptions.put(entry.getKey(), entry.getValue()); + } + } + return childOptions.build(); + } /** * Enumerates all the entries in this profile, including those that were inherited from another @@ -201,4 +221,109 @@ default List getDurationList( */ @NonNull SortedSet> entrySet(); + + @NonNull + @Override + default DriverExecutionProfile withBoolean(@NonNull DriverOption option, boolean value) { + return DerivedExecutionProfile.with(this, option, value); + } + + @NonNull + @Override + default DriverExecutionProfile withBooleanList( + @NonNull DriverOption option, @NonNull List value) { + return DerivedExecutionProfile.with(this, option, value); + } + + @NonNull + @Override + default DriverExecutionProfile withInt(@NonNull DriverOption option, int value) { + return DerivedExecutionProfile.with(this, option, value); + } + + @NonNull + @Override + default DriverExecutionProfile withIntList( + @NonNull DriverOption option, @NonNull List value) { + return DerivedExecutionProfile.with(this, option, value); + } + + @NonNull + @Override + default DriverExecutionProfile withLong(@NonNull DriverOption option, long value) { + return DerivedExecutionProfile.with(this, option, value); + } + + @NonNull + @Override + default DriverExecutionProfile withLongList( + @NonNull DriverOption option, @NonNull List value) { + return DerivedExecutionProfile.with(this, option, value); + } + + @NonNull + @Override + default DriverExecutionProfile withDouble(@NonNull DriverOption option, double value) { + return DerivedExecutionProfile.with(this, option, value); + } + + @NonNull + @Override + default DriverExecutionProfile withDoubleList( + @NonNull DriverOption option, @NonNull List value) { + return DerivedExecutionProfile.with(this, option, value); + } + + @NonNull + @Override + default DriverExecutionProfile withString(@NonNull DriverOption option, @NonNull String value) { + return DerivedExecutionProfile.with(this, option, value); + } + + @NonNull + @Override + default DriverExecutionProfile withStringList( + @NonNull DriverOption option, @NonNull List value) { + return DerivedExecutionProfile.with(this, option, value); + } + + @NonNull + @Override + default DriverExecutionProfile withStringMap( + @NonNull DriverOption option, @NonNull Map value) { + return DerivedExecutionProfile.with(this, option, value); + } + + @NonNull + @Override + default DriverExecutionProfile withBytes(@NonNull DriverOption option, long value) { + return DerivedExecutionProfile.with(this, option, value); + } + + @NonNull + @Override + default DriverExecutionProfile withBytesList( + @NonNull DriverOption option, @NonNull List value) { + return DerivedExecutionProfile.with(this, option, value); + } + + @NonNull + @Override + default DriverExecutionProfile withDuration( + @NonNull DriverOption option, @NonNull Duration value) { + return DerivedExecutionProfile.with(this, option, value); + } + + @NonNull + @Override + default DriverExecutionProfile withDurationList( + @NonNull DriverOption option, @NonNull List value) { + return DerivedExecutionProfile.with(this, option, value); + } + + @NonNull + @Override + default DriverExecutionProfile without(@NonNull DriverOption option) { + return DerivedExecutionProfile.without(this, option); + } } diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/config/DriverOption.java b/core/src/main/java/com/datastax/oss/driver/api/core/config/DriverOption.java index 3213dc4b2ad..2f15b701f36 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/config/DriverOption.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/config/DriverOption.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/config/OngoingConfigOptions.java b/core/src/main/java/com/datastax/oss/driver/api/core/config/OngoingConfigOptions.java index 0345150d770..2c931bbfa91 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/config/OngoingConfigOptions.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/config/OngoingConfigOptions.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -19,6 +21,7 @@ import java.time.Duration; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; /** An object where config options can be set programmatically. */ public interface OngoingConfigOptions> { @@ -59,6 +62,15 @@ default SelfT withClass(@NonNull DriverOption option, @NonNull Class value) { return withString(option, value.getName()); } + /** + * Note that this is just a shortcut to call {@link #withStringList(DriverOption, List)} with + * class names obtained from {@link Class#getName()}. + */ + @NonNull + default SelfT withClassList(@NonNull DriverOption option, @NonNull List> values) { + return withStringList(option, values.stream().map(Class::getName).collect(Collectors.toList())); + } + @NonNull SelfT withStringList(@NonNull DriverOption option, @NonNull List value); diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/config/OptionsMap.java b/core/src/main/java/com/datastax/oss/driver/api/core/config/OptionsMap.java new file mode 100644 index 00000000000..98faf3e590c --- /dev/null +++ b/core/src/main/java/com/datastax/oss/driver/api/core/config/OptionsMap.java @@ -0,0 +1,403 @@ +/* + * 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 com.datastax.oss.driver.api.core.config; + +import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableList; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.time.Duration; +import java.time.temporal.ChronoUnit; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.function.Consumer; +import net.jcip.annotations.Immutable; +import net.jcip.annotations.ThreadSafe; + +/** + * An in-memory repository of config options, for use with {@link + * DriverConfigLoader#fromMap(OptionsMap)}. + * + *

This class is intended for clients who wish to assemble the driver configuration in memory, + * instead of loading it from configuration files. Note that {@link #driverDefaults()} can be used + * to pre-initialize the map with the driver's built-in defaults. + * + *

It functions like a two-dimensional map indexed by execution profile and option. All methods + * have a profile-less variant that applies to the default profile, for example {@link #get(String, + * TypedDriverOption)} and {@link #get(TypedDriverOption)}. Options are represented by {@link + * TypedDriverOption}, which allows this class to enforce additional type-safety guarantees (an + * option can only be set to a value of its intended type). + * + *

This class is mutable and thread-safe. Live changes are reflected in real time to the driver + * session(s) that use this configuration. + * + * @since 4.6.0 + */ +@ThreadSafe +public class OptionsMap implements Serializable { + + private static final long serialVersionUID = 1; + + /** + * Creates a new instance that contains the driver's default configuration. + * + *

This will produce a configuration that is equivalent to the {@code reference.conf} file + * bundled with the driver (however, this method does not load any file, and doesn't require + * Typesafe config in the classpath). + */ + @NonNull + public static OptionsMap driverDefaults() { + OptionsMap source = new OptionsMap(); + fillWithDriverDefaults(source); + return source; + } + + private final ConcurrentHashMap> map; + + private final List> changeListeners = new CopyOnWriteArrayList<>(); + + public OptionsMap() { + this(new ConcurrentHashMap<>()); + } + + private OptionsMap(ConcurrentHashMap> map) { + this.map = map; + } + + /** + * Associates the specified value for the specified option, in the specified execution profile. + * + * @return the previous value associated with {@code option}, or {@code null} if the option was + * not defined. + */ + @Nullable + public ValueT put( + @NonNull String profile, @NonNull TypedDriverOption option, @NonNull ValueT value) { + Objects.requireNonNull(option, "option"); + Objects.requireNonNull(value, "value"); + Object previous = getProfileMap(profile).put(option.getRawOption(), value); + if (!value.equals(previous)) { + for (Consumer listener : changeListeners) { + listener.accept(this); + } + } + return cast(previous); + } + + /** + * Associates the specified value for the specified option, in the default execution profile. + * + * @return the previous value associated with {@code option}, or {@code null} if the option was + * not defined. + */ + @Nullable + public ValueT put(@NonNull TypedDriverOption option, @NonNull ValueT value) { + return put(DriverExecutionProfile.DEFAULT_NAME, option, value); + } + + /** + * Returns the value to which the specified option is mapped in the specified profile, or {@code + * null} if the option is not defined. + */ + @Nullable + public ValueT get(@NonNull String profile, @NonNull TypedDriverOption option) { + Objects.requireNonNull(option, "option"); + Object result = getProfileMap(profile).get(option.getRawOption()); + return cast(result); + } + + /** + * Returns the value to which the specified option is mapped in the default profile, or {@code + * null} if the option is not defined. + */ + @Nullable + public ValueT get(@NonNull TypedDriverOption option) { + return get(DriverExecutionProfile.DEFAULT_NAME, option); + } + + /** + * Removes the specified option from the specified profile. + * + * @return the previous value associated with {@code option}, or {@code null} if the option was + * not defined. + */ + @Nullable + public ValueT remove( + @NonNull String profile, @NonNull TypedDriverOption option) { + Objects.requireNonNull(option, "option"); + Object previous = getProfileMap(profile).remove(option.getRawOption()); + if (previous != null) { + for (Consumer listener : changeListeners) { + listener.accept(this); + } + } + return cast(previous); + } + + /** + * Removes the specified option from the default profile. + * + * @return the previous value associated with {@code option}, or {@code null} if the option was + * not defined. + */ + @Nullable + public ValueT remove(@NonNull TypedDriverOption option) { + return remove(DriverExecutionProfile.DEFAULT_NAME, option); + } + + /** + * Registers a listener that will get notified when this object changes. + * + *

This is mostly for internal use by the driver. Note that listeners are transient, and not + * taken into account by {@link #equals(Object)} and {@link #hashCode()}. + */ + public void addChangeListener(@NonNull Consumer listener) { + changeListeners.add(Objects.requireNonNull(listener)); + } + + /** + * Unregisters a listener that was previously registered with {@link + * #addChangeListener(Consumer)}. + * + * @return {@code true} if the listener was indeed registered for this object. + */ + public boolean removeChangeListener(@NonNull Consumer listener) { + return changeListeners.remove(Objects.requireNonNull(listener)); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } else if (other instanceof OptionsMap) { + OptionsMap that = (OptionsMap) other; + return this.map.equals(that.map); + } else { + return false; + } + } + + @Override + public int hashCode() { + return map.hashCode(); + } + + /** + * Returns a live view of this object, using the driver's untyped {@link DriverOption}. + * + *

This is intended for internal usage by the driver. Modifying the resulting map is strongly + * discouraged, as it could break the type-safety guarantees provided by the public methods. + */ + @NonNull + protected Map> asRawMap() { + return map; + } + + @NonNull + private Map getProfileMap(@NonNull String profile) { + Objects.requireNonNull(profile, "profile"); + return map.computeIfAbsent(profile, p -> new ConcurrentHashMap<>()); + } + + // Isolate the suppressed warning for retrieval. The cast should always succeed unless the user + // messes with asMap() directly. + @SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"}) + @Nullable + private ValueT cast(@Nullable Object value) { + return (ValueT) value; + } + + /** + * This object gets replaced by an internal proxy for serialization. + * + * @serialData the serialized form of the {@code Map>} used to + * store options internally (listeners are transient). + */ + private Object writeReplace() { + return new SerializationProxy(this.map); + } + + // Should never be called since we serialize a proxy + @SuppressWarnings("UnusedVariable") + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Proxy required"); + } + + protected static void fillWithDriverDefaults(OptionsMap map) { + Duration initQueryTimeout = Duration.ofSeconds(5); + Duration requestTimeout = Duration.ofSeconds(2); + int requestPageSize = 5000; + int continuousMaxPages = 0; + int continuousMaxPagesPerSecond = 0; + int continuousMaxEnqueuedPages = 4; + + // Sorted by order of appearance in reference.conf: + + // Skip CONFIG_RELOAD_INTERVAL because the map-based config doesn't need periodic reloading + map.put(TypedDriverOption.REQUEST_TIMEOUT, requestTimeout); + map.put(TypedDriverOption.REQUEST_CONSISTENCY, "LOCAL_ONE"); + map.put(TypedDriverOption.REQUEST_PAGE_SIZE, requestPageSize); + map.put(TypedDriverOption.REQUEST_SERIAL_CONSISTENCY, "SERIAL"); + map.put(TypedDriverOption.REQUEST_DEFAULT_IDEMPOTENCE, false); + map.put(TypedDriverOption.GRAPH_TRAVERSAL_SOURCE, "g"); + map.put(TypedDriverOption.LOAD_BALANCING_POLICY_CLASS, "DefaultLoadBalancingPolicy"); + map.put(TypedDriverOption.LOAD_BALANCING_POLICY_SLOW_AVOIDANCE, true); + map.put(TypedDriverOption.SESSION_LEAK_THRESHOLD, 4); + map.put(TypedDriverOption.CONNECTION_CONNECT_TIMEOUT, Duration.ofSeconds(5)); + map.put(TypedDriverOption.CONNECTION_INIT_QUERY_TIMEOUT, initQueryTimeout); + map.put(TypedDriverOption.CONNECTION_SET_KEYSPACE_TIMEOUT, initQueryTimeout); + map.put(TypedDriverOption.CONNECTION_POOL_LOCAL_SIZE, 1); + map.put(TypedDriverOption.CONNECTION_POOL_REMOTE_SIZE, 1); + map.put(TypedDriverOption.CONNECTION_MAX_REQUESTS, 1024); + map.put(TypedDriverOption.CONNECTION_MAX_ORPHAN_REQUESTS, 256); + map.put(TypedDriverOption.CONNECTION_WARN_INIT_ERROR, true); + map.put(TypedDriverOption.RECONNECT_ON_INIT, false); + map.put(TypedDriverOption.RECONNECTION_POLICY_CLASS, "ExponentialReconnectionPolicy"); + map.put(TypedDriverOption.RECONNECTION_BASE_DELAY, Duration.ofSeconds(1)); + map.put(TypedDriverOption.RECONNECTION_MAX_DELAY, Duration.ofSeconds(60)); + map.put(TypedDriverOption.RETRY_POLICY_CLASS, "DefaultRetryPolicy"); + map.put(TypedDriverOption.SPECULATIVE_EXECUTION_POLICY_CLASS, "NoSpeculativeExecutionPolicy"); + map.put(TypedDriverOption.TIMESTAMP_GENERATOR_CLASS, "AtomicTimestampGenerator"); + map.put(TypedDriverOption.TIMESTAMP_GENERATOR_DRIFT_WARNING_THRESHOLD, Duration.ofSeconds(1)); + map.put(TypedDriverOption.TIMESTAMP_GENERATOR_DRIFT_WARNING_INTERVAL, Duration.ofSeconds(10)); + map.put(TypedDriverOption.TIMESTAMP_GENERATOR_FORCE_JAVA_CLOCK, false); + map.put(TypedDriverOption.REQUEST_THROTTLER_CLASS, "PassThroughRequestThrottler"); + map.put(TypedDriverOption.ADDRESS_TRANSLATOR_CLASS, "PassThroughAddressTranslator"); + map.put(TypedDriverOption.RESOLVE_CONTACT_POINTS, true); + map.put(TypedDriverOption.PROTOCOL_MAX_FRAME_LENGTH, 256L * 1024 * 1024); + map.put(TypedDriverOption.REQUEST_WARN_IF_SET_KEYSPACE, true); + map.put(TypedDriverOption.REQUEST_TRACE_ATTEMPTS, 5); + map.put(TypedDriverOption.REQUEST_TRACE_INTERVAL, Duration.ofMillis(3)); + map.put(TypedDriverOption.REQUEST_TRACE_CONSISTENCY, "ONE"); + map.put(TypedDriverOption.REQUEST_LOG_WARNINGS, true); + map.put(TypedDriverOption.GRAPH_PAGING_ENABLED, "AUTO"); + map.put(TypedDriverOption.GRAPH_CONTINUOUS_PAGING_PAGE_SIZE, requestPageSize); + map.put(TypedDriverOption.GRAPH_CONTINUOUS_PAGING_MAX_PAGES, continuousMaxPages); + map.put( + TypedDriverOption.GRAPH_CONTINUOUS_PAGING_MAX_PAGES_PER_SECOND, + continuousMaxPagesPerSecond); + map.put( + TypedDriverOption.GRAPH_CONTINUOUS_PAGING_MAX_ENQUEUED_PAGES, continuousMaxEnqueuedPages); + map.put(TypedDriverOption.CONTINUOUS_PAGING_PAGE_SIZE, requestPageSize); + map.put(TypedDriverOption.CONTINUOUS_PAGING_PAGE_SIZE_BYTES, false); + map.put(TypedDriverOption.CONTINUOUS_PAGING_MAX_PAGES, continuousMaxPages); + map.put(TypedDriverOption.CONTINUOUS_PAGING_MAX_PAGES_PER_SECOND, continuousMaxPagesPerSecond); + map.put(TypedDriverOption.CONTINUOUS_PAGING_MAX_ENQUEUED_PAGES, continuousMaxEnqueuedPages); + map.put(TypedDriverOption.CONTINUOUS_PAGING_TIMEOUT_FIRST_PAGE, Duration.ofSeconds(2)); + map.put(TypedDriverOption.CONTINUOUS_PAGING_TIMEOUT_OTHER_PAGES, Duration.ofSeconds(1)); + map.put(TypedDriverOption.MONITOR_REPORTING_ENABLED, true); + map.put(TypedDriverOption.METRICS_SESSION_ENABLED, Collections.emptyList()); + map.put(TypedDriverOption.METRICS_SESSION_CQL_REQUESTS_HIGHEST, Duration.ofSeconds(3)); + map.put(TypedDriverOption.METRICS_SESSION_CQL_REQUESTS_LOWEST, Duration.ofMillis(1)); + map.put(TypedDriverOption.METRICS_SESSION_CQL_REQUESTS_DIGITS, 3); + map.put(TypedDriverOption.METRICS_SESSION_CQL_REQUESTS_INTERVAL, Duration.ofMinutes(5)); + map.put(TypedDriverOption.METRICS_SESSION_THROTTLING_HIGHEST, Duration.ofSeconds(3)); + map.put(TypedDriverOption.METRICS_SESSION_THROTTLING_LOWEST, Duration.ofMillis(1)); + map.put(TypedDriverOption.METRICS_SESSION_THROTTLING_DIGITS, 3); + map.put(TypedDriverOption.METRICS_SESSION_THROTTLING_INTERVAL, Duration.ofMinutes(5)); + map.put( + TypedDriverOption.CONTINUOUS_PAGING_METRICS_SESSION_CQL_REQUESTS_HIGHEST, + Duration.ofMinutes(2)); + map.put( + TypedDriverOption.CONTINUOUS_PAGING_METRICS_SESSION_CQL_REQUESTS_LOWEST, + Duration.ofMillis(10)); + map.put(TypedDriverOption.CONTINUOUS_PAGING_METRICS_SESSION_CQL_REQUESTS_DIGITS, 3); + map.put( + TypedDriverOption.CONTINUOUS_PAGING_METRICS_SESSION_CQL_REQUESTS_INTERVAL, + Duration.ofMinutes(5)); + map.put(TypedDriverOption.METRICS_FACTORY_CLASS, "DefaultMetricsFactory"); + map.put(TypedDriverOption.METRICS_ID_GENERATOR_CLASS, "DefaultMetricIdGenerator"); + map.put(TypedDriverOption.METRICS_SESSION_GRAPH_REQUESTS_HIGHEST, Duration.ofSeconds(12)); + map.put(TypedDriverOption.METRICS_SESSION_GRAPH_REQUESTS_LOWEST, Duration.ofMillis(1)); + map.put(TypedDriverOption.METRICS_SESSION_GRAPH_REQUESTS_DIGITS, 3); + map.put(TypedDriverOption.METRICS_SESSION_GRAPH_REQUESTS_INTERVAL, Duration.ofMinutes(5)); + map.put(TypedDriverOption.METRICS_NODE_ENABLED, Collections.emptyList()); + map.put(TypedDriverOption.METRICS_NODE_CQL_MESSAGES_HIGHEST, Duration.ofSeconds(3)); + map.put(TypedDriverOption.METRICS_NODE_CQL_MESSAGES_LOWEST, Duration.ofMillis(1)); + map.put(TypedDriverOption.METRICS_NODE_CQL_MESSAGES_DIGITS, 3); + map.put(TypedDriverOption.METRICS_NODE_CQL_MESSAGES_INTERVAL, Duration.ofMinutes(5)); + map.put(TypedDriverOption.METRICS_NODE_GRAPH_MESSAGES_HIGHEST, Duration.ofSeconds(3)); + map.put(TypedDriverOption.METRICS_NODE_GRAPH_MESSAGES_LOWEST, Duration.ofMillis(1)); + map.put(TypedDriverOption.METRICS_NODE_GRAPH_MESSAGES_DIGITS, 3); + map.put(TypedDriverOption.METRICS_NODE_GRAPH_MESSAGES_INTERVAL, Duration.ofMinutes(5)); + map.put(TypedDriverOption.METRICS_NODE_EXPIRE_AFTER, Duration.ofHours(1)); + map.put(TypedDriverOption.SOCKET_TCP_NODELAY, true); + map.put(TypedDriverOption.HEARTBEAT_INTERVAL, Duration.ofSeconds(30)); + map.put(TypedDriverOption.HEARTBEAT_TIMEOUT, initQueryTimeout); + map.put(TypedDriverOption.METADATA_TOPOLOGY_WINDOW, Duration.ofSeconds(1)); + map.put(TypedDriverOption.METADATA_TOPOLOGY_MAX_EVENTS, 20); + map.put(TypedDriverOption.METADATA_SCHEMA_ENABLED, true); + map.put( + TypedDriverOption.METADATA_SCHEMA_REFRESHED_KEYSPACES, + ImmutableList.of("!system", "!/^system_.*/", "!/^dse_.*/", "!solr_admin", "!OpsCenter")); + map.put(TypedDriverOption.METADATA_SCHEMA_REQUEST_TIMEOUT, requestTimeout); + map.put(TypedDriverOption.METADATA_SCHEMA_REQUEST_PAGE_SIZE, requestPageSize); + map.put(TypedDriverOption.METADATA_SCHEMA_WINDOW, Duration.ofSeconds(1)); + map.put(TypedDriverOption.METADATA_SCHEMA_MAX_EVENTS, 20); + map.put(TypedDriverOption.METADATA_TOKEN_MAP_ENABLED, true); + map.put(TypedDriverOption.CONTROL_CONNECTION_TIMEOUT, initQueryTimeout); + map.put(TypedDriverOption.CONTROL_CONNECTION_AGREEMENT_INTERVAL, Duration.ofMillis(200)); + map.put(TypedDriverOption.CONTROL_CONNECTION_AGREEMENT_TIMEOUT, Duration.ofSeconds(10)); + map.put(TypedDriverOption.CONTROL_CONNECTION_AGREEMENT_WARN, true); + map.put(TypedDriverOption.PREPARE_ON_ALL_NODES, true); + map.put(TypedDriverOption.REPREPARE_ENABLED, true); + map.put(TypedDriverOption.REPREPARE_CHECK_SYSTEM_TABLE, false); + map.put(TypedDriverOption.REPREPARE_MAX_STATEMENTS, 0); + map.put(TypedDriverOption.REPREPARE_MAX_PARALLELISM, 100); + map.put(TypedDriverOption.REPREPARE_TIMEOUT, initQueryTimeout); + map.put(TypedDriverOption.NETTY_DAEMON, false); + map.put(TypedDriverOption.NETTY_IO_SIZE, 0); + map.put(TypedDriverOption.NETTY_IO_SHUTDOWN_QUIET_PERIOD, 2); + map.put(TypedDriverOption.NETTY_IO_SHUTDOWN_TIMEOUT, 15); + map.put(TypedDriverOption.NETTY_IO_SHUTDOWN_UNIT, "SECONDS"); + map.put(TypedDriverOption.NETTY_ADMIN_SIZE, 2); + map.put(TypedDriverOption.NETTY_ADMIN_SHUTDOWN_QUIET_PERIOD, 2); + map.put(TypedDriverOption.NETTY_ADMIN_SHUTDOWN_TIMEOUT, 15); + map.put(TypedDriverOption.NETTY_ADMIN_SHUTDOWN_UNIT, "SECONDS"); + map.put(TypedDriverOption.NETTY_TIMER_TICK_DURATION, Duration.ofMillis(100)); + map.put(TypedDriverOption.NETTY_TIMER_TICKS_PER_WHEEL, 2048); + map.put(TypedDriverOption.COALESCER_INTERVAL, Duration.of(10, ChronoUnit.MICROS)); + map.put(TypedDriverOption.LOAD_BALANCING_DC_FAILOVER_MAX_NODES_PER_REMOTE_DC, 0); + map.put(TypedDriverOption.LOAD_BALANCING_DC_FAILOVER_ALLOW_FOR_LOCAL_CONSISTENCY_LEVELS, false); + map.put(TypedDriverOption.METRICS_GENERATE_AGGREGABLE_HISTOGRAMS, true); + map.put( + TypedDriverOption.LOAD_BALANCING_DC_FAILOVER_PREFERRED_REMOTE_DCS, ImmutableList.of("")); + } + + @Immutable + private static class SerializationProxy implements Serializable { + + private static final long serialVersionUID = 1L; + + private final ConcurrentHashMap> map; + + private SerializationProxy(ConcurrentHashMap> map) { + this.map = map; + } + + private Object readResolve() { + return new OptionsMap(map); + } + } +} diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/config/ProgrammaticDriverConfigLoaderBuilder.java b/core/src/main/java/com/datastax/oss/driver/api/core/config/ProgrammaticDriverConfigLoaderBuilder.java index 2f6aa64485e..c3ae1d1bf5b 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/config/ProgrammaticDriverConfigLoaderBuilder.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/config/ProgrammaticDriverConfigLoaderBuilder.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/config/TypedDriverOption.java b/core/src/main/java/com/datastax/oss/driver/api/core/config/TypedDriverOption.java new file mode 100644 index 00000000000..182753300e7 --- /dev/null +++ b/core/src/main/java/com/datastax/oss/driver/api/core/config/TypedDriverOption.java @@ -0,0 +1,944 @@ +/* + * 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 com.datastax.oss.driver.api.core.config; + +import com.datastax.dse.driver.api.core.config.DseDriverOption; +import com.datastax.oss.driver.api.core.type.reflect.GenericType; +import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableList; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.time.Duration; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.StringJoiner; + +/** + * A type-safe wrapper around {@link DriverOption}, that encodes the intended value type of each + * option. + * + *

This type was introduced in conjunction with {@link DriverConfigLoader#fromMap(OptionsMap)}. + * Unfortunately, for backward compatibility reasons, it wasn't possible to retrofit the rest of the + * driver to use it; therefore the APIs used to read the configuration, such as {@link DriverConfig} + * and {@link DriverExecutionProfile}, still use the untyped {@link DriverOption}. + * + * @since 4.6.0 + */ +public class TypedDriverOption { + + private static volatile Iterable> builtInValues; + + /** + * Returns the list of all built-in options known to the driver codebase; in other words, all the + * {@link TypedDriverOption} constants defined on this class. + * + *

Note that 3rd-party driver extensions might define their own {@link TypedDriverOption} + * constants for custom options. + * + *

This method uses reflection to introspect all the constants on this class; the result is + * computed lazily on the first invocation, and then cached for future calls. + */ + public static Iterable> builtInValues() { + if (builtInValues == null) { + builtInValues = introspectBuiltInValues(); + } + return builtInValues; + } + + private final DriverOption rawOption; + private final GenericType expectedType; + + public TypedDriverOption( + @NonNull DriverOption rawOption, @NonNull GenericType expectedType) { + this.rawOption = Objects.requireNonNull(rawOption); + this.expectedType = Objects.requireNonNull(expectedType); + } + + @NonNull + public DriverOption getRawOption() { + return rawOption; + } + + @NonNull + public GenericType getExpectedType() { + return expectedType; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } else if (other instanceof TypedDriverOption) { + TypedDriverOption that = (TypedDriverOption) other; + return this.rawOption.equals(that.rawOption) && this.expectedType.equals(that.expectedType); + } else { + return false; + } + } + + @Override + public int hashCode() { + return Objects.hash(rawOption, expectedType); + } + + @Override + public String toString() { + return new StringJoiner(", ", TypedDriverOption.class.getSimpleName() + "[", "]") + .add("rawOption=" + rawOption) + .add("expectedType=" + expectedType) + .toString(); + } + + /** The contact points to use for the initial connection to the cluster. */ + public static final TypedDriverOption> CONTACT_POINTS = + new TypedDriverOption<>(DefaultDriverOption.CONTACT_POINTS, GenericType.listOf(String.class)); + /** A name that uniquely identifies the driver instance. */ + public static final TypedDriverOption SESSION_NAME = + new TypedDriverOption<>(DefaultDriverOption.SESSION_NAME, GenericType.STRING); + /** The name of the keyspace that the session should initially be connected to. */ + public static final TypedDriverOption SESSION_KEYSPACE = + new TypedDriverOption<>(DefaultDriverOption.SESSION_KEYSPACE, GenericType.STRING); + /** How often the driver tries to reload the configuration. */ + public static final TypedDriverOption CONFIG_RELOAD_INTERVAL = + new TypedDriverOption<>(DefaultDriverOption.CONFIG_RELOAD_INTERVAL, GenericType.DURATION); + /** How long the driver waits for a request to complete. */ + public static final TypedDriverOption REQUEST_TIMEOUT = + new TypedDriverOption<>(DefaultDriverOption.REQUEST_TIMEOUT, GenericType.DURATION); + /** The consistency level. */ + public static final TypedDriverOption REQUEST_CONSISTENCY = + new TypedDriverOption<>(DefaultDriverOption.REQUEST_CONSISTENCY, GenericType.STRING); + /** The page size. */ + public static final TypedDriverOption REQUEST_PAGE_SIZE = + new TypedDriverOption<>(DefaultDriverOption.REQUEST_PAGE_SIZE, GenericType.INTEGER); + /** The serial consistency level. */ + public static final TypedDriverOption REQUEST_SERIAL_CONSISTENCY = + new TypedDriverOption<>(DefaultDriverOption.REQUEST_SERIAL_CONSISTENCY, GenericType.STRING); + /** The default idempotence of a request. */ + public static final TypedDriverOption REQUEST_DEFAULT_IDEMPOTENCE = + new TypedDriverOption<>(DefaultDriverOption.REQUEST_DEFAULT_IDEMPOTENCE, GenericType.BOOLEAN); + /** The class of the load balancing policy. */ + public static final TypedDriverOption LOAD_BALANCING_POLICY_CLASS = + new TypedDriverOption<>(DefaultDriverOption.LOAD_BALANCING_POLICY_CLASS, GenericType.STRING); + /** The datacenter that is considered "local". */ + public static final TypedDriverOption LOAD_BALANCING_LOCAL_DATACENTER = + new TypedDriverOption<>( + DefaultDriverOption.LOAD_BALANCING_LOCAL_DATACENTER, GenericType.STRING); + /** + * A custom filter to include/exclude nodes. + * + * @deprecated Use {@link #LOAD_BALANCING_DISTANCE_EVALUATOR_CLASS} instead. + */ + @Deprecated + public static final TypedDriverOption LOAD_BALANCING_FILTER_CLASS = + new TypedDriverOption<>(DefaultDriverOption.LOAD_BALANCING_FILTER_CLASS, GenericType.STRING); + /** + * The class name of a custom {@link + * com.datastax.oss.driver.api.core.loadbalancing.NodeDistanceEvaluator}. + */ + public static final TypedDriverOption LOAD_BALANCING_DISTANCE_EVALUATOR_CLASS = + new TypedDriverOption<>( + DefaultDriverOption.LOAD_BALANCING_DISTANCE_EVALUATOR_CLASS, GenericType.STRING); + /** The timeout to use for internal queries that run as part of the initialization process. */ + public static final TypedDriverOption CONNECTION_INIT_QUERY_TIMEOUT = + new TypedDriverOption<>( + DefaultDriverOption.CONNECTION_INIT_QUERY_TIMEOUT, GenericType.DURATION); + /** The timeout to use when the driver changes the keyspace on a connection at runtime. */ + public static final TypedDriverOption CONNECTION_SET_KEYSPACE_TIMEOUT = + new TypedDriverOption<>( + DefaultDriverOption.CONNECTION_SET_KEYSPACE_TIMEOUT, GenericType.DURATION); + /** The maximum number of requests that can be executed concurrently on a connection. */ + public static final TypedDriverOption CONNECTION_MAX_REQUESTS = + new TypedDriverOption<>(DefaultDriverOption.CONNECTION_MAX_REQUESTS, GenericType.INTEGER); + /** The maximum number of "orphaned" requests before a connection gets closed automatically. */ + public static final TypedDriverOption CONNECTION_MAX_ORPHAN_REQUESTS = + new TypedDriverOption<>( + DefaultDriverOption.CONNECTION_MAX_ORPHAN_REQUESTS, GenericType.INTEGER); + /** Whether to log non-fatal errors when the driver tries to open a new connection. */ + public static final TypedDriverOption CONNECTION_WARN_INIT_ERROR = + new TypedDriverOption<>(DefaultDriverOption.CONNECTION_WARN_INIT_ERROR, GenericType.BOOLEAN); + /** The number of connections in the LOCAL pool. */ + public static final TypedDriverOption CONNECTION_POOL_LOCAL_SIZE = + new TypedDriverOption<>(DefaultDriverOption.CONNECTION_POOL_LOCAL_SIZE, GenericType.INTEGER); + /** The number of connections in the REMOTE pool. */ + public static final TypedDriverOption CONNECTION_POOL_REMOTE_SIZE = + new TypedDriverOption<>(DefaultDriverOption.CONNECTION_POOL_REMOTE_SIZE, GenericType.INTEGER); + /** + * Whether to schedule reconnection attempts if all contact points are unreachable on the first + * initialization attempt. + */ + public static final TypedDriverOption RECONNECT_ON_INIT = + new TypedDriverOption<>(DefaultDriverOption.RECONNECT_ON_INIT, GenericType.BOOLEAN); + /** The class of the reconnection policy. */ + public static final TypedDriverOption RECONNECTION_POLICY_CLASS = + new TypedDriverOption<>(DefaultDriverOption.RECONNECTION_POLICY_CLASS, GenericType.STRING); + /** Base delay for computing time between reconnection attempts. */ + public static final TypedDriverOption RECONNECTION_BASE_DELAY = + new TypedDriverOption<>(DefaultDriverOption.RECONNECTION_BASE_DELAY, GenericType.DURATION); + /** Maximum delay between reconnection attempts. */ + public static final TypedDriverOption RECONNECTION_MAX_DELAY = + new TypedDriverOption<>(DefaultDriverOption.RECONNECTION_MAX_DELAY, GenericType.DURATION); + /** The class of the retry policy. */ + public static final TypedDriverOption RETRY_POLICY_CLASS = + new TypedDriverOption<>(DefaultDriverOption.RETRY_POLICY_CLASS, GenericType.STRING); + /** The class of the speculative execution policy. */ + public static final TypedDriverOption SPECULATIVE_EXECUTION_POLICY_CLASS = + new TypedDriverOption<>( + DefaultDriverOption.SPECULATIVE_EXECUTION_POLICY_CLASS, GenericType.STRING); + /** The maximum number of executions. */ + public static final TypedDriverOption SPECULATIVE_EXECUTION_MAX = + new TypedDriverOption<>(DefaultDriverOption.SPECULATIVE_EXECUTION_MAX, GenericType.INTEGER); + /** The delay between each execution. */ + public static final TypedDriverOption SPECULATIVE_EXECUTION_DELAY = + new TypedDriverOption<>( + DefaultDriverOption.SPECULATIVE_EXECUTION_DELAY, GenericType.DURATION); + /** The class of the authentication provider. */ + public static final TypedDriverOption AUTH_PROVIDER_CLASS = + new TypedDriverOption<>(DefaultDriverOption.AUTH_PROVIDER_CLASS, GenericType.STRING); + /** Plain text auth provider username. */ + public static final TypedDriverOption AUTH_PROVIDER_USER_NAME = + new TypedDriverOption<>(DefaultDriverOption.AUTH_PROVIDER_USER_NAME, GenericType.STRING); + /** Plain text auth provider password. */ + public static final TypedDriverOption AUTH_PROVIDER_PASSWORD = + new TypedDriverOption<>(DefaultDriverOption.AUTH_PROVIDER_PASSWORD, GenericType.STRING); + /** The class of the SSL Engine Factory. */ + public static final TypedDriverOption SSL_ENGINE_FACTORY_CLASS = + new TypedDriverOption<>(DefaultDriverOption.SSL_ENGINE_FACTORY_CLASS, GenericType.STRING); + /** The cipher suites to enable when creating an SSLEngine for a connection. */ + public static final TypedDriverOption> SSL_CIPHER_SUITES = + new TypedDriverOption<>( + DefaultDriverOption.SSL_CIPHER_SUITES, GenericType.listOf(String.class)); + /** + * Whether or not to require validation that the hostname of the server certificate's common name + * matches the hostname of the server being connected to. + */ + public static final TypedDriverOption SSL_HOSTNAME_VALIDATION = + new TypedDriverOption<>(DefaultDriverOption.SSL_HOSTNAME_VALIDATION, GenericType.BOOLEAN); + + public static final TypedDriverOption SSL_ALLOW_DNS_REVERSE_LOOKUP_SAN = + new TypedDriverOption<>( + DefaultDriverOption.SSL_ALLOW_DNS_REVERSE_LOOKUP_SAN, GenericType.BOOLEAN); + /** The location of the keystore file. */ + public static final TypedDriverOption SSL_KEYSTORE_PATH = + new TypedDriverOption<>(DefaultDriverOption.SSL_KEYSTORE_PATH, GenericType.STRING); + /** The keystore password. */ + public static final TypedDriverOption SSL_KEYSTORE_PASSWORD = + new TypedDriverOption<>(DefaultDriverOption.SSL_KEYSTORE_PASSWORD, GenericType.STRING); + + /** The duration between attempts to reload the keystore. */ + public static final TypedDriverOption SSL_KEYSTORE_RELOAD_INTERVAL = + new TypedDriverOption<>( + DefaultDriverOption.SSL_KEYSTORE_RELOAD_INTERVAL, GenericType.DURATION); + + /** The location of the truststore file. */ + public static final TypedDriverOption SSL_TRUSTSTORE_PATH = + new TypedDriverOption<>(DefaultDriverOption.SSL_TRUSTSTORE_PATH, GenericType.STRING); + /** The truststore password. */ + public static final TypedDriverOption SSL_TRUSTSTORE_PASSWORD = + new TypedDriverOption<>(DefaultDriverOption.SSL_TRUSTSTORE_PASSWORD, GenericType.STRING); + /** The class of the generator that assigns a microsecond timestamp to each request. */ + public static final TypedDriverOption TIMESTAMP_GENERATOR_CLASS = + new TypedDriverOption<>(DefaultDriverOption.TIMESTAMP_GENERATOR_CLASS, GenericType.STRING); + /** Whether to force the driver to use Java's millisecond-precision system clock. */ + public static final TypedDriverOption TIMESTAMP_GENERATOR_FORCE_JAVA_CLOCK = + new TypedDriverOption<>( + DefaultDriverOption.TIMESTAMP_GENERATOR_FORCE_JAVA_CLOCK, GenericType.BOOLEAN); + /** How far in the future timestamps are allowed to drift before the warning is logged. */ + public static final TypedDriverOption TIMESTAMP_GENERATOR_DRIFT_WARNING_THRESHOLD = + new TypedDriverOption<>( + DefaultDriverOption.TIMESTAMP_GENERATOR_DRIFT_WARNING_THRESHOLD, GenericType.DURATION); + /** How often the warning will be logged if timestamps keep drifting above the threshold. */ + public static final TypedDriverOption TIMESTAMP_GENERATOR_DRIFT_WARNING_INTERVAL = + new TypedDriverOption<>( + DefaultDriverOption.TIMESTAMP_GENERATOR_DRIFT_WARNING_INTERVAL, GenericType.DURATION); + + /** + * The class of a session-wide component that tracks the outcome of requests. + * + * @deprecated Use {@link #REQUEST_TRACKER_CLASSES} instead. + */ + @Deprecated + public static final TypedDriverOption REQUEST_TRACKER_CLASS = + new TypedDriverOption<>(DefaultDriverOption.REQUEST_TRACKER_CLASS, GenericType.STRING); + + /** The classes of session-wide components that track the outcome of requests. */ + public static final TypedDriverOption> REQUEST_TRACKER_CLASSES = + new TypedDriverOption<>( + DefaultDriverOption.REQUEST_TRACKER_CLASSES, GenericType.listOf(String.class)); + + /** The class of a session-wide component that generates request IDs. */ + public static final TypedDriverOption REQUEST_ID_GENERATOR_CLASS = + new TypedDriverOption<>(DefaultDriverOption.REQUEST_ID_GENERATOR_CLASS, GenericType.STRING); + + /** Whether to log successful requests. */ + public static final TypedDriverOption REQUEST_LOGGER_SUCCESS_ENABLED = + new TypedDriverOption<>( + DefaultDriverOption.REQUEST_LOGGER_SUCCESS_ENABLED, GenericType.BOOLEAN); + /** The threshold to classify a successful request as "slow". */ + public static final TypedDriverOption REQUEST_LOGGER_SLOW_THRESHOLD = + new TypedDriverOption<>( + DefaultDriverOption.REQUEST_LOGGER_SLOW_THRESHOLD, GenericType.DURATION); + /** Whether to log slow requests. */ + public static final TypedDriverOption REQUEST_LOGGER_SLOW_ENABLED = + new TypedDriverOption<>(DefaultDriverOption.REQUEST_LOGGER_SLOW_ENABLED, GenericType.BOOLEAN); + /** Whether to log failed requests. */ + public static final TypedDriverOption REQUEST_LOGGER_ERROR_ENABLED = + new TypedDriverOption<>( + DefaultDriverOption.REQUEST_LOGGER_ERROR_ENABLED, GenericType.BOOLEAN); + /** The maximum length of the query string in the log message. */ + public static final TypedDriverOption REQUEST_LOGGER_MAX_QUERY_LENGTH = + new TypedDriverOption<>( + DefaultDriverOption.REQUEST_LOGGER_MAX_QUERY_LENGTH, GenericType.INTEGER); + /** Whether to log bound values in addition to the query string. */ + public static final TypedDriverOption REQUEST_LOGGER_VALUES = + new TypedDriverOption<>(DefaultDriverOption.REQUEST_LOGGER_VALUES, GenericType.BOOLEAN); + /** The maximum length for bound values in the log message. */ + public static final TypedDriverOption REQUEST_LOGGER_MAX_VALUE_LENGTH = + new TypedDriverOption<>( + DefaultDriverOption.REQUEST_LOGGER_MAX_VALUE_LENGTH, GenericType.INTEGER); + /** The maximum number of bound values to log. */ + public static final TypedDriverOption REQUEST_LOGGER_MAX_VALUES = + new TypedDriverOption<>(DefaultDriverOption.REQUEST_LOGGER_MAX_VALUES, GenericType.INTEGER); + /** Whether to log stack traces for failed queries. */ + public static final TypedDriverOption REQUEST_LOGGER_STACK_TRACES = + new TypedDriverOption<>(DefaultDriverOption.REQUEST_LOGGER_STACK_TRACES, GenericType.BOOLEAN); + /** + * The class of a session-wide component that controls the rate at which requests are executed. + */ + public static final TypedDriverOption REQUEST_THROTTLER_CLASS = + new TypedDriverOption<>(DefaultDriverOption.REQUEST_THROTTLER_CLASS, GenericType.STRING); + /** The maximum number of requests that are allowed to execute in parallel. */ + public static final TypedDriverOption REQUEST_THROTTLER_MAX_CONCURRENT_REQUESTS = + new TypedDriverOption<>( + DefaultDriverOption.REQUEST_THROTTLER_MAX_CONCURRENT_REQUESTS, GenericType.INTEGER); + /** The maximum allowed request rate. */ + public static final TypedDriverOption REQUEST_THROTTLER_MAX_REQUESTS_PER_SECOND = + new TypedDriverOption<>( + DefaultDriverOption.REQUEST_THROTTLER_MAX_REQUESTS_PER_SECOND, GenericType.INTEGER); + /** + * The maximum number of requests that can be enqueued when the throttling threshold is exceeded. + */ + public static final TypedDriverOption REQUEST_THROTTLER_MAX_QUEUE_SIZE = + new TypedDriverOption<>( + DefaultDriverOption.REQUEST_THROTTLER_MAX_QUEUE_SIZE, GenericType.INTEGER); + /** How often the throttler attempts to dequeue requests. */ + public static final TypedDriverOption REQUEST_THROTTLER_DRAIN_INTERVAL = + new TypedDriverOption<>( + DefaultDriverOption.REQUEST_THROTTLER_DRAIN_INTERVAL, GenericType.DURATION); + + /** + * The class of a session-wide component that listens for node state changes. + * + * @deprecated Use {@link #METADATA_NODE_STATE_LISTENER_CLASSES} instead. + */ + @Deprecated + public static final TypedDriverOption METADATA_NODE_STATE_LISTENER_CLASS = + new TypedDriverOption<>( + DefaultDriverOption.METADATA_NODE_STATE_LISTENER_CLASS, GenericType.STRING); + + /** + * The class of a session-wide component that listens for schema changes. + * + * @deprecated Use {@link #METADATA_SCHEMA_CHANGE_LISTENER_CLASSES} instead. + */ + @Deprecated + public static final TypedDriverOption METADATA_SCHEMA_CHANGE_LISTENER_CLASS = + new TypedDriverOption<>( + DefaultDriverOption.METADATA_SCHEMA_CHANGE_LISTENER_CLASS, GenericType.STRING); + + /** The classes of session-wide components that listen for node state changes. */ + public static final TypedDriverOption> METADATA_NODE_STATE_LISTENER_CLASSES = + new TypedDriverOption<>( + DefaultDriverOption.METADATA_NODE_STATE_LISTENER_CLASSES, + GenericType.listOf(String.class)); + + /** The classes of session-wide components that listen for schema changes. */ + public static final TypedDriverOption> METADATA_SCHEMA_CHANGE_LISTENER_CLASSES = + new TypedDriverOption<>( + DefaultDriverOption.METADATA_SCHEMA_CHANGE_LISTENER_CLASSES, + GenericType.listOf(String.class)); + + /** + * The class of the address translator to use to convert the addresses sent by Cassandra nodes + * into ones that the driver uses to connect. + */ + public static final TypedDriverOption ADDRESS_TRANSLATOR_CLASS = + new TypedDriverOption<>(DefaultDriverOption.ADDRESS_TRANSLATOR_CLASS, GenericType.STRING); + /** The native protocol version to use. */ + public static final TypedDriverOption PROTOCOL_VERSION = + new TypedDriverOption<>(DefaultDriverOption.PROTOCOL_VERSION, GenericType.STRING); + /** The name of the algorithm used to compress protocol frames. */ + public static final TypedDriverOption PROTOCOL_COMPRESSION = + new TypedDriverOption<>(DefaultDriverOption.PROTOCOL_COMPRESSION, GenericType.STRING); + /** The maximum length, in bytes, of the frames supported by the driver. */ + public static final TypedDriverOption PROTOCOL_MAX_FRAME_LENGTH = + new TypedDriverOption<>(DefaultDriverOption.PROTOCOL_MAX_FRAME_LENGTH, GenericType.LONG); + /** + * Whether a warning is logged when a request (such as a CQL `USE ...`) changes the active + * keyspace. + */ + public static final TypedDriverOption REQUEST_WARN_IF_SET_KEYSPACE = + new TypedDriverOption<>( + DefaultDriverOption.REQUEST_WARN_IF_SET_KEYSPACE, GenericType.BOOLEAN); + /** How many times the driver will attempt to fetch the query trace if it is not ready yet. */ + public static final TypedDriverOption REQUEST_TRACE_ATTEMPTS = + new TypedDriverOption<>(DefaultDriverOption.REQUEST_TRACE_ATTEMPTS, GenericType.INTEGER); + /** The interval between each attempt. */ + public static final TypedDriverOption REQUEST_TRACE_INTERVAL = + new TypedDriverOption<>(DefaultDriverOption.REQUEST_TRACE_INTERVAL, GenericType.DURATION); + /** The consistency level to use for trace queries. */ + public static final TypedDriverOption REQUEST_TRACE_CONSISTENCY = + new TypedDriverOption<>(DefaultDriverOption.REQUEST_TRACE_CONSISTENCY, GenericType.STRING); + /** Whether or not to publish aggregable histogram for metrics */ + public static final TypedDriverOption METRICS_GENERATE_AGGREGABLE_HISTOGRAMS = + new TypedDriverOption<>( + DefaultDriverOption.METRICS_GENERATE_AGGREGABLE_HISTOGRAMS, GenericType.BOOLEAN); + /** List of enabled session-level metrics. */ + public static final TypedDriverOption> METRICS_SESSION_ENABLED = + new TypedDriverOption<>( + DefaultDriverOption.METRICS_SESSION_ENABLED, GenericType.listOf(String.class)); + /** List of enabled node-level metrics. */ + public static final TypedDriverOption> METRICS_NODE_ENABLED = + new TypedDriverOption<>( + DefaultDriverOption.METRICS_NODE_ENABLED, GenericType.listOf(String.class)); + /** The largest latency that we expect to record for requests. */ + public static final TypedDriverOption METRICS_SESSION_CQL_REQUESTS_HIGHEST = + new TypedDriverOption<>( + DefaultDriverOption.METRICS_SESSION_CQL_REQUESTS_HIGHEST, GenericType.DURATION); + /** The shortest latency that we expect to record for requests. */ + public static final TypedDriverOption METRICS_SESSION_CQL_REQUESTS_LOWEST = + new TypedDriverOption<>( + DefaultDriverOption.METRICS_SESSION_CQL_REQUESTS_LOWEST, GenericType.DURATION); + /** Optional service-level objectives to meet, as a list of latencies to track. */ + public static final TypedDriverOption> METRICS_SESSION_CQL_REQUESTS_SLO = + new TypedDriverOption<>( + DefaultDriverOption.METRICS_SESSION_CQL_REQUESTS_SLO, + GenericType.listOf(GenericType.DURATION)); + /** Optional pre-defined percentile of cql requests to publish, as a list of percentiles . */ + public static final TypedDriverOption> + METRICS_SESSION_CQL_REQUESTS_PUBLISH_PERCENTILES = + new TypedDriverOption<>( + DefaultDriverOption.METRICS_SESSION_CQL_REQUESTS_PUBLISH_PERCENTILES, + GenericType.listOf(GenericType.DOUBLE)); + /** + * The number of significant decimal digits to which internal structures will maintain for + * requests. + */ + public static final TypedDriverOption METRICS_SESSION_CQL_REQUESTS_DIGITS = + new TypedDriverOption<>( + DefaultDriverOption.METRICS_SESSION_CQL_REQUESTS_DIGITS, GenericType.INTEGER); + /** The interval at which percentile data is refreshed for requests. */ + public static final TypedDriverOption METRICS_SESSION_CQL_REQUESTS_INTERVAL = + new TypedDriverOption<>( + DefaultDriverOption.METRICS_SESSION_CQL_REQUESTS_INTERVAL, GenericType.DURATION); + /** The largest latency that we expect to record for throttling. */ + public static final TypedDriverOption METRICS_SESSION_THROTTLING_HIGHEST = + new TypedDriverOption<>( + DefaultDriverOption.METRICS_SESSION_THROTTLING_HIGHEST, GenericType.DURATION); + /** The shortest latency that we expect to record for throttling. */ + public static final TypedDriverOption METRICS_SESSION_THROTTLING_LOWEST = + new TypedDriverOption<>( + DefaultDriverOption.METRICS_SESSION_THROTTLING_LOWEST, GenericType.DURATION); + /** Optional service-level objectives to meet, as a list of latencies to track. */ + public static final TypedDriverOption> METRICS_SESSION_THROTTLING_SLO = + new TypedDriverOption<>( + DefaultDriverOption.METRICS_SESSION_THROTTLING_SLO, + GenericType.listOf(GenericType.DURATION)); + /** Optional pre-defined percentile of throttling delay to publish, as a list of percentiles . */ + public static final TypedDriverOption> + METRICS_SESSION_THROTTLING_PUBLISH_PERCENTILES = + new TypedDriverOption<>( + DefaultDriverOption.METRICS_SESSION_THROTTLING_PUBLISH_PERCENTILES, + GenericType.listOf(GenericType.DOUBLE)); + /** + * The number of significant decimal digits to which internal structures will maintain for + * throttling. + */ + public static final TypedDriverOption METRICS_SESSION_THROTTLING_DIGITS = + new TypedDriverOption<>( + DefaultDriverOption.METRICS_SESSION_THROTTLING_DIGITS, GenericType.INTEGER); + /** The interval at which percentile data is refreshed for throttling. */ + public static final TypedDriverOption METRICS_SESSION_THROTTLING_INTERVAL = + new TypedDriverOption<>( + DefaultDriverOption.METRICS_SESSION_THROTTLING_INTERVAL, GenericType.DURATION); + /** The largest latency that we expect to record for requests. */ + public static final TypedDriverOption METRICS_NODE_CQL_MESSAGES_HIGHEST = + new TypedDriverOption<>( + DefaultDriverOption.METRICS_NODE_CQL_MESSAGES_HIGHEST, GenericType.DURATION); + /** The shortest latency that we expect to record for requests. */ + public static final TypedDriverOption METRICS_NODE_CQL_MESSAGES_LOWEST = + new TypedDriverOption<>( + DefaultDriverOption.METRICS_NODE_CQL_MESSAGES_LOWEST, GenericType.DURATION); + /** Optional service-level objectives to meet, as a list of latencies to track. */ + public static final TypedDriverOption> METRICS_NODE_CQL_MESSAGES_SLO = + new TypedDriverOption<>( + DefaultDriverOption.METRICS_NODE_CQL_MESSAGES_SLO, + GenericType.listOf(GenericType.DURATION)); + /** Optional pre-defined percentile of node cql messages to publish, as a list of percentiles . */ + public static final TypedDriverOption> + METRICS_NODE_CQL_MESSAGES_PUBLISH_PERCENTILES = + new TypedDriverOption<>( + DefaultDriverOption.METRICS_NODE_CQL_MESSAGES_PUBLISH_PERCENTILES, + GenericType.listOf(GenericType.DOUBLE)); + /** + * The number of significant decimal digits to which internal structures will maintain for + * requests. + */ + public static final TypedDriverOption METRICS_NODE_CQL_MESSAGES_DIGITS = + new TypedDriverOption<>( + DefaultDriverOption.METRICS_NODE_CQL_MESSAGES_DIGITS, GenericType.INTEGER); + /** The interval at which percentile data is refreshed for requests. */ + public static final TypedDriverOption METRICS_NODE_CQL_MESSAGES_INTERVAL = + new TypedDriverOption<>( + DefaultDriverOption.METRICS_NODE_CQL_MESSAGES_INTERVAL, GenericType.DURATION); + /** Whether or not to disable the Nagle algorithm. */ + public static final TypedDriverOption SOCKET_TCP_NODELAY = + new TypedDriverOption<>(DefaultDriverOption.SOCKET_TCP_NODELAY, GenericType.BOOLEAN); + /** Whether or not to enable TCP keep-alive probes. */ + public static final TypedDriverOption SOCKET_KEEP_ALIVE = + new TypedDriverOption<>(DefaultDriverOption.SOCKET_KEEP_ALIVE, GenericType.BOOLEAN); + /** Whether or not to allow address reuse. */ + public static final TypedDriverOption SOCKET_REUSE_ADDRESS = + new TypedDriverOption<>(DefaultDriverOption.SOCKET_REUSE_ADDRESS, GenericType.BOOLEAN); + /** Sets the linger interval. */ + public static final TypedDriverOption SOCKET_LINGER_INTERVAL = + new TypedDriverOption<>(DefaultDriverOption.SOCKET_LINGER_INTERVAL, GenericType.INTEGER); + /** Sets a hint to the size of the underlying buffers for incoming network I/O. */ + public static final TypedDriverOption SOCKET_RECEIVE_BUFFER_SIZE = + new TypedDriverOption<>(DefaultDriverOption.SOCKET_RECEIVE_BUFFER_SIZE, GenericType.INTEGER); + /** Sets a hint to the size of the underlying buffers for outgoing network I/O. */ + public static final TypedDriverOption SOCKET_SEND_BUFFER_SIZE = + new TypedDriverOption<>(DefaultDriverOption.SOCKET_SEND_BUFFER_SIZE, GenericType.INTEGER); + /** The connection heartbeat interval. */ + public static final TypedDriverOption HEARTBEAT_INTERVAL = + new TypedDriverOption<>(DefaultDriverOption.HEARTBEAT_INTERVAL, GenericType.DURATION); + /** How long the driver waits for the response to a heartbeat. */ + public static final TypedDriverOption HEARTBEAT_TIMEOUT = + new TypedDriverOption<>(DefaultDriverOption.HEARTBEAT_TIMEOUT, GenericType.DURATION); + /** How long the driver waits to propagate a Topology event. */ + public static final TypedDriverOption METADATA_TOPOLOGY_WINDOW = + new TypedDriverOption<>(DefaultDriverOption.METADATA_TOPOLOGY_WINDOW, GenericType.DURATION); + /** The maximum number of events that can accumulate. */ + public static final TypedDriverOption METADATA_TOPOLOGY_MAX_EVENTS = + new TypedDriverOption<>( + DefaultDriverOption.METADATA_TOPOLOGY_MAX_EVENTS, GenericType.INTEGER); + /** Whether schema metadata is enabled. */ + public static final TypedDriverOption METADATA_SCHEMA_ENABLED = + new TypedDriverOption<>(DefaultDriverOption.METADATA_SCHEMA_ENABLED, GenericType.BOOLEAN); + /** The timeout for the requests to the schema tables. */ + public static final TypedDriverOption METADATA_SCHEMA_REQUEST_TIMEOUT = + new TypedDriverOption<>( + DefaultDriverOption.METADATA_SCHEMA_REQUEST_TIMEOUT, GenericType.DURATION); + /** The page size for the requests to the schema tables. */ + public static final TypedDriverOption METADATA_SCHEMA_REQUEST_PAGE_SIZE = + new TypedDriverOption<>( + DefaultDriverOption.METADATA_SCHEMA_REQUEST_PAGE_SIZE, GenericType.INTEGER); + /** The list of keyspaces for which schema and token metadata should be maintained. */ + public static final TypedDriverOption> METADATA_SCHEMA_REFRESHED_KEYSPACES = + new TypedDriverOption<>( + DefaultDriverOption.METADATA_SCHEMA_REFRESHED_KEYSPACES, + GenericType.listOf(String.class)); + /** How long the driver waits to apply a refresh. */ + public static final TypedDriverOption METADATA_SCHEMA_WINDOW = + new TypedDriverOption<>(DefaultDriverOption.METADATA_SCHEMA_WINDOW, GenericType.DURATION); + /** The maximum number of refreshes that can accumulate. */ + public static final TypedDriverOption METADATA_SCHEMA_MAX_EVENTS = + new TypedDriverOption<>(DefaultDriverOption.METADATA_SCHEMA_MAX_EVENTS, GenericType.INTEGER); + /** Whether token metadata is enabled. */ + public static final TypedDriverOption METADATA_TOKEN_MAP_ENABLED = + new TypedDriverOption<>(DefaultDriverOption.METADATA_TOKEN_MAP_ENABLED, GenericType.BOOLEAN); + /** How long the driver waits for responses to control queries. */ + public static final TypedDriverOption CONTROL_CONNECTION_TIMEOUT = + new TypedDriverOption<>(DefaultDriverOption.CONTROL_CONNECTION_TIMEOUT, GenericType.DURATION); + /** The interval between each schema agreement check attempt. */ + public static final TypedDriverOption CONTROL_CONNECTION_AGREEMENT_INTERVAL = + new TypedDriverOption<>( + DefaultDriverOption.CONTROL_CONNECTION_AGREEMENT_INTERVAL, GenericType.DURATION); + /** The timeout after which schema agreement fails. */ + public static final TypedDriverOption CONTROL_CONNECTION_AGREEMENT_TIMEOUT = + new TypedDriverOption<>( + DefaultDriverOption.CONTROL_CONNECTION_AGREEMENT_TIMEOUT, GenericType.DURATION); + /** Whether to log a warning if schema agreement fails. */ + public static final TypedDriverOption CONTROL_CONNECTION_AGREEMENT_WARN = + new TypedDriverOption<>( + DefaultDriverOption.CONTROL_CONNECTION_AGREEMENT_WARN, GenericType.BOOLEAN); + /** Whether `Session.prepare` calls should be sent to all nodes in the cluster. */ + public static final TypedDriverOption PREPARE_ON_ALL_NODES = + new TypedDriverOption<>(DefaultDriverOption.PREPARE_ON_ALL_NODES, GenericType.BOOLEAN); + /** Whether the driver tries to prepare on new nodes at all. */ + public static final TypedDriverOption REPREPARE_ENABLED = + new TypedDriverOption<>(DefaultDriverOption.REPREPARE_ENABLED, GenericType.BOOLEAN); + /** Whether to check `system.prepared_statements` on the target node before repreparing. */ + public static final TypedDriverOption REPREPARE_CHECK_SYSTEM_TABLE = + new TypedDriverOption<>( + DefaultDriverOption.REPREPARE_CHECK_SYSTEM_TABLE, GenericType.BOOLEAN); + /** The maximum number of statements that should be reprepared. */ + public static final TypedDriverOption REPREPARE_MAX_STATEMENTS = + new TypedDriverOption<>(DefaultDriverOption.REPREPARE_MAX_STATEMENTS, GenericType.INTEGER); + /** The maximum number of concurrent requests when repreparing. */ + public static final TypedDriverOption REPREPARE_MAX_PARALLELISM = + new TypedDriverOption<>(DefaultDriverOption.REPREPARE_MAX_PARALLELISM, GenericType.INTEGER); + /** The request timeout when repreparing. */ + public static final TypedDriverOption REPREPARE_TIMEOUT = + new TypedDriverOption<>(DefaultDriverOption.REPREPARE_TIMEOUT, GenericType.DURATION); + /** Whether the prepared statements cache use weak values. */ + public static final TypedDriverOption PREPARED_CACHE_WEAK_VALUES = + new TypedDriverOption<>(DefaultDriverOption.PREPARED_CACHE_WEAK_VALUES, GenericType.BOOLEAN); + /** The number of threads in the I/O group. */ + public static final TypedDriverOption NETTY_IO_SIZE = + new TypedDriverOption<>(DefaultDriverOption.NETTY_IO_SIZE, GenericType.INTEGER); + /** Quiet period for I/O group shutdown. */ + public static final TypedDriverOption NETTY_IO_SHUTDOWN_QUIET_PERIOD = + new TypedDriverOption<>( + DefaultDriverOption.NETTY_IO_SHUTDOWN_QUIET_PERIOD, GenericType.INTEGER); + /** Max time to wait for I/O group shutdown. */ + public static final TypedDriverOption NETTY_IO_SHUTDOWN_TIMEOUT = + new TypedDriverOption<>(DefaultDriverOption.NETTY_IO_SHUTDOWN_TIMEOUT, GenericType.INTEGER); + /** Units for I/O group quiet period and timeout. */ + public static final TypedDriverOption NETTY_IO_SHUTDOWN_UNIT = + new TypedDriverOption<>(DefaultDriverOption.NETTY_IO_SHUTDOWN_UNIT, GenericType.STRING); + /** The number of threads in the Admin group. */ + public static final TypedDriverOption NETTY_ADMIN_SIZE = + new TypedDriverOption<>(DefaultDriverOption.NETTY_ADMIN_SIZE, GenericType.INTEGER); + /** Quiet period for admin group shutdown. */ + public static final TypedDriverOption NETTY_ADMIN_SHUTDOWN_QUIET_PERIOD = + new TypedDriverOption<>( + DefaultDriverOption.NETTY_ADMIN_SHUTDOWN_QUIET_PERIOD, GenericType.INTEGER); + /** Max time to wait for admin group shutdown. */ + public static final TypedDriverOption NETTY_ADMIN_SHUTDOWN_TIMEOUT = + new TypedDriverOption<>( + DefaultDriverOption.NETTY_ADMIN_SHUTDOWN_TIMEOUT, GenericType.INTEGER); + /** Units for admin group quiet period and timeout. */ + public static final TypedDriverOption NETTY_ADMIN_SHUTDOWN_UNIT = + new TypedDriverOption<>(DefaultDriverOption.NETTY_ADMIN_SHUTDOWN_UNIT, GenericType.STRING); + /** @deprecated This option was removed in version 4.6.1. */ + @Deprecated + public static final TypedDriverOption COALESCER_MAX_RUNS = + new TypedDriverOption<>(DefaultDriverOption.COALESCER_MAX_RUNS, GenericType.INTEGER); + /** The coalescer reschedule interval. */ + public static final TypedDriverOption COALESCER_INTERVAL = + new TypedDriverOption<>(DefaultDriverOption.COALESCER_INTERVAL, GenericType.DURATION); + /** Whether to resolve the addresses passed to `basic.contact-points`. */ + public static final TypedDriverOption RESOLVE_CONTACT_POINTS = + new TypedDriverOption<>(DefaultDriverOption.RESOLVE_CONTACT_POINTS, GenericType.BOOLEAN); + /** + * This is how frequent the timer should wake up to check for timed-out tasks or speculative + * executions. + */ + public static final TypedDriverOption NETTY_TIMER_TICK_DURATION = + new TypedDriverOption<>(DefaultDriverOption.NETTY_TIMER_TICK_DURATION, GenericType.DURATION); + /** Number of ticks in the Timer wheel. */ + public static final TypedDriverOption NETTY_TIMER_TICKS_PER_WHEEL = + new TypedDriverOption<>(DefaultDriverOption.NETTY_TIMER_TICKS_PER_WHEEL, GenericType.INTEGER); + /** + * Whether logging of server warnings generated during query execution should be disabled by the + * driver. + */ + public static final TypedDriverOption REQUEST_LOG_WARNINGS = + new TypedDriverOption<>(DefaultDriverOption.REQUEST_LOG_WARNINGS, GenericType.BOOLEAN); + /** Whether the threads created by the driver should be daemon threads. */ + public static final TypedDriverOption NETTY_DAEMON = + new TypedDriverOption<>(DefaultDriverOption.NETTY_DAEMON, GenericType.BOOLEAN); + /** + * The location of the cloud secure bundle used to connect to DataStax Apache Cassandra as a + * service. + */ + public static final TypedDriverOption CLOUD_SECURE_CONNECT_BUNDLE = + new TypedDriverOption<>(DefaultDriverOption.CLOUD_SECURE_CONNECT_BUNDLE, GenericType.STRING); + /** Whether the slow replica avoidance should be enabled in the default LBP. */ + public static final TypedDriverOption LOAD_BALANCING_POLICY_SLOW_AVOIDANCE = + new TypedDriverOption<>( + DefaultDriverOption.LOAD_BALANCING_POLICY_SLOW_AVOIDANCE, GenericType.BOOLEAN); + /** The timeout to use when establishing driver connections. */ + public static final TypedDriverOption CONNECTION_CONNECT_TIMEOUT = + new TypedDriverOption<>(DefaultDriverOption.CONNECTION_CONNECT_TIMEOUT, GenericType.DURATION); + /** The maximum number of live sessions that are allowed to coexist in a given VM. */ + public static final TypedDriverOption SESSION_LEAK_THRESHOLD = + new TypedDriverOption<>(DefaultDriverOption.SESSION_LEAK_THRESHOLD, GenericType.INTEGER); + + /** The name of the application using the session. */ + public static final TypedDriverOption APPLICATION_NAME = + new TypedDriverOption<>(DseDriverOption.APPLICATION_NAME, GenericType.STRING); + /** The version of the application using the session. */ + public static final TypedDriverOption APPLICATION_VERSION = + new TypedDriverOption<>(DseDriverOption.APPLICATION_VERSION, GenericType.STRING); + /** Proxy authentication for GSSAPI authentication: allows to login as another user or role. */ + public static final TypedDriverOption AUTH_PROVIDER_AUTHORIZATION_ID = + new TypedDriverOption<>(DseDriverOption.AUTH_PROVIDER_AUTHORIZATION_ID, GenericType.STRING); + /** Service name for GSSAPI authentication. */ + public static final TypedDriverOption AUTH_PROVIDER_SERVICE = + new TypedDriverOption<>(DseDriverOption.AUTH_PROVIDER_SERVICE, GenericType.STRING); + /** Login configuration for GSSAPI authentication. */ + public static final TypedDriverOption AUTH_PROVIDER_LOGIN_CONFIGURATION = + new TypedDriverOption<>( + DseDriverOption.AUTH_PROVIDER_LOGIN_CONFIGURATION, GenericType.STRING); + /** Internal SASL properties, if any, such as QOP, for GSSAPI authentication. */ + public static final TypedDriverOption> AUTH_PROVIDER_SASL_PROPERTIES = + new TypedDriverOption<>( + DseDriverOption.AUTH_PROVIDER_SASL_PROPERTIES, + GenericType.mapOf(GenericType.STRING, GenericType.STRING)); + /** The page size for continuous paging. */ + public static final TypedDriverOption CONTINUOUS_PAGING_PAGE_SIZE = + new TypedDriverOption<>(DseDriverOption.CONTINUOUS_PAGING_PAGE_SIZE, GenericType.INTEGER); + /** + * Whether {@link #CONTINUOUS_PAGING_PAGE_SIZE} should be interpreted in number of rows or bytes. + */ + public static final TypedDriverOption CONTINUOUS_PAGING_PAGE_SIZE_BYTES = + new TypedDriverOption<>( + DseDriverOption.CONTINUOUS_PAGING_PAGE_SIZE_BYTES, GenericType.BOOLEAN); + /** The maximum number of continuous pages to return. */ + public static final TypedDriverOption CONTINUOUS_PAGING_MAX_PAGES = + new TypedDriverOption<>(DseDriverOption.CONTINUOUS_PAGING_MAX_PAGES, GenericType.INTEGER); + /** The maximum number of continuous pages per second. */ + public static final TypedDriverOption CONTINUOUS_PAGING_MAX_PAGES_PER_SECOND = + new TypedDriverOption<>( + DseDriverOption.CONTINUOUS_PAGING_MAX_PAGES_PER_SECOND, GenericType.INTEGER); + /** The maximum number of continuous pages that can be stored in the local queue. */ + public static final TypedDriverOption CONTINUOUS_PAGING_MAX_ENQUEUED_PAGES = + new TypedDriverOption<>( + DseDriverOption.CONTINUOUS_PAGING_MAX_ENQUEUED_PAGES, GenericType.INTEGER); + /** How long to wait for the coordinator to send the first continuous page. */ + public static final TypedDriverOption CONTINUOUS_PAGING_TIMEOUT_FIRST_PAGE = + new TypedDriverOption<>( + DseDriverOption.CONTINUOUS_PAGING_TIMEOUT_FIRST_PAGE, GenericType.DURATION); + /** How long to wait for the coordinator to send subsequent continuous pages. */ + public static final TypedDriverOption CONTINUOUS_PAGING_TIMEOUT_OTHER_PAGES = + new TypedDriverOption<>( + DseDriverOption.CONTINUOUS_PAGING_TIMEOUT_OTHER_PAGES, GenericType.DURATION); + /** The largest latency that we expect to record for continuous requests. */ + public static final TypedDriverOption + CONTINUOUS_PAGING_METRICS_SESSION_CQL_REQUESTS_HIGHEST = + new TypedDriverOption<>( + DseDriverOption.CONTINUOUS_PAGING_METRICS_SESSION_CQL_REQUESTS_HIGHEST, + GenericType.DURATION); + /** The shortest latency that we expect to record for continuous requests. */ + public static final TypedDriverOption + CONTINUOUS_PAGING_METRICS_SESSION_CQL_REQUESTS_LOWEST = + new TypedDriverOption<>( + DseDriverOption.CONTINUOUS_PAGING_METRICS_SESSION_CQL_REQUESTS_LOWEST, + GenericType.DURATION); + /** Optional service-level objectives to meet, as a list of latencies to track. */ + public static final TypedDriverOption> + CONTINUOUS_PAGING_METRICS_SESSION_CQL_REQUESTS_SLO = + new TypedDriverOption<>( + DseDriverOption.CONTINUOUS_PAGING_METRICS_SESSION_CQL_REQUESTS_SLO, + GenericType.listOf(GenericType.DURATION)); + /** + * Optional pre-defined percentile of continuous paging cql requests to publish, as a list of + * percentiles . + */ + public static final TypedDriverOption> + CONTINUOUS_PAGING_METRICS_SESSION_CQL_REQUESTS_PUBLISH_PERCENTILES = + new TypedDriverOption<>( + DseDriverOption.CONTINUOUS_PAGING_METRICS_SESSION_CQL_REQUESTS_PUBLISH_PERCENTILES, + GenericType.listOf(GenericType.DOUBLE)); + /** + * The number of significant decimal digits to which internal structures will maintain for + * continuous requests. + */ + public static final TypedDriverOption + CONTINUOUS_PAGING_METRICS_SESSION_CQL_REQUESTS_DIGITS = + new TypedDriverOption<>( + DseDriverOption.CONTINUOUS_PAGING_METRICS_SESSION_CQL_REQUESTS_DIGITS, + GenericType.INTEGER); + /** The interval at which percentile data is refreshed for continuous requests. */ + public static final TypedDriverOption + CONTINUOUS_PAGING_METRICS_SESSION_CQL_REQUESTS_INTERVAL = + new TypedDriverOption<>( + DseDriverOption.CONTINUOUS_PAGING_METRICS_SESSION_CQL_REQUESTS_INTERVAL, + GenericType.DURATION); + /** The read consistency level to use for graph statements. */ + public static final TypedDriverOption GRAPH_READ_CONSISTENCY_LEVEL = + new TypedDriverOption<>(DseDriverOption.GRAPH_READ_CONSISTENCY_LEVEL, GenericType.STRING); + /** The write consistency level to use for graph statements. */ + public static final TypedDriverOption GRAPH_WRITE_CONSISTENCY_LEVEL = + new TypedDriverOption<>(DseDriverOption.GRAPH_WRITE_CONSISTENCY_LEVEL, GenericType.STRING); + /** The traversal source to use for graph statements. */ + public static final TypedDriverOption GRAPH_TRAVERSAL_SOURCE = + new TypedDriverOption<>(DseDriverOption.GRAPH_TRAVERSAL_SOURCE, GenericType.STRING); + /** + * The sub-protocol the driver will use to communicate with DSE Graph, on top of the Cassandra + * native protocol. + */ + public static final TypedDriverOption GRAPH_SUB_PROTOCOL = + new TypedDriverOption<>(DseDriverOption.GRAPH_SUB_PROTOCOL, GenericType.STRING); + /** Whether a script statement represents a system query. */ + public static final TypedDriverOption GRAPH_IS_SYSTEM_QUERY = + new TypedDriverOption<>(DseDriverOption.GRAPH_IS_SYSTEM_QUERY, GenericType.BOOLEAN); + /** The name of the graph targeted by graph statements. */ + public static final TypedDriverOption GRAPH_NAME = + new TypedDriverOption<>(DseDriverOption.GRAPH_NAME, GenericType.STRING); + /** How long the driver waits for a graph request to complete. */ + public static final TypedDriverOption GRAPH_TIMEOUT = + new TypedDriverOption<>(DseDriverOption.GRAPH_TIMEOUT, GenericType.DURATION); + /** Whether to send events for Insights monitoring. */ + public static final TypedDriverOption MONITOR_REPORTING_ENABLED = + new TypedDriverOption<>(DseDriverOption.MONITOR_REPORTING_ENABLED, GenericType.BOOLEAN); + /** Whether to enable paging for Graph queries. */ + public static final TypedDriverOption GRAPH_PAGING_ENABLED = + new TypedDriverOption<>(DseDriverOption.GRAPH_PAGING_ENABLED, GenericType.STRING); + /** The page size for Graph continuous paging. */ + public static final TypedDriverOption GRAPH_CONTINUOUS_PAGING_PAGE_SIZE = + new TypedDriverOption<>( + DseDriverOption.GRAPH_CONTINUOUS_PAGING_PAGE_SIZE, GenericType.INTEGER); + /** The maximum number of Graph continuous pages to return. */ + public static final TypedDriverOption GRAPH_CONTINUOUS_PAGING_MAX_PAGES = + new TypedDriverOption<>( + DseDriverOption.GRAPH_CONTINUOUS_PAGING_MAX_PAGES, GenericType.INTEGER); + /** The maximum number of Graph continuous pages per second. */ + public static final TypedDriverOption GRAPH_CONTINUOUS_PAGING_MAX_PAGES_PER_SECOND = + new TypedDriverOption<>( + DseDriverOption.GRAPH_CONTINUOUS_PAGING_MAX_PAGES_PER_SECOND, GenericType.INTEGER); + /** The maximum number of Graph continuous pages that can be stored in the local queue. */ + public static final TypedDriverOption GRAPH_CONTINUOUS_PAGING_MAX_ENQUEUED_PAGES = + new TypedDriverOption<>( + DseDriverOption.GRAPH_CONTINUOUS_PAGING_MAX_ENQUEUED_PAGES, GenericType.INTEGER); + /** The largest latency that we expect to record for graph requests. */ + public static final TypedDriverOption METRICS_SESSION_GRAPH_REQUESTS_HIGHEST = + new TypedDriverOption<>( + DseDriverOption.METRICS_SESSION_GRAPH_REQUESTS_HIGHEST, GenericType.DURATION); + /** The shortest latency that we expect to record for graph requests. */ + public static final TypedDriverOption METRICS_SESSION_GRAPH_REQUESTS_LOWEST = + new TypedDriverOption<>( + DseDriverOption.METRICS_SESSION_GRAPH_REQUESTS_LOWEST, GenericType.DURATION); + /** Optional service-level objectives to meet, as a list of latencies to track. */ + public static final TypedDriverOption> METRICS_SESSION_GRAPH_REQUESTS_SLO = + new TypedDriverOption<>( + DseDriverOption.METRICS_SESSION_GRAPH_REQUESTS_SLO, + GenericType.listOf(GenericType.DURATION)); + /** Optional pre-defined percentile of graph requests to publish, as a list of percentiles . */ + public static final TypedDriverOption> + METRICS_SESSION_GRAPH_REQUESTS_PUBLISH_PERCENTILES = + new TypedDriverOption<>( + DseDriverOption.METRICS_SESSION_GRAPH_REQUESTS_PUBLISH_PERCENTILES, + GenericType.listOf(GenericType.DOUBLE)); + /** + * The number of significant decimal digits to which internal structures will maintain for graph + * requests. + */ + public static final TypedDriverOption METRICS_SESSION_GRAPH_REQUESTS_DIGITS = + new TypedDriverOption<>( + DseDriverOption.METRICS_SESSION_GRAPH_REQUESTS_DIGITS, GenericType.INTEGER); + /** The interval at which percentile data is refreshed for graph requests. */ + public static final TypedDriverOption METRICS_SESSION_GRAPH_REQUESTS_INTERVAL = + new TypedDriverOption<>( + DseDriverOption.METRICS_SESSION_GRAPH_REQUESTS_INTERVAL, GenericType.DURATION); + /** The largest latency that we expect to record for graph requests. */ + public static final TypedDriverOption METRICS_NODE_GRAPH_MESSAGES_HIGHEST = + new TypedDriverOption<>( + DseDriverOption.METRICS_NODE_GRAPH_MESSAGES_HIGHEST, GenericType.DURATION); + /** The shortest latency that we expect to record for graph requests. */ + public static final TypedDriverOption METRICS_NODE_GRAPH_MESSAGES_LOWEST = + new TypedDriverOption<>( + DseDriverOption.METRICS_NODE_GRAPH_MESSAGES_LOWEST, GenericType.DURATION); + /** Optional service-level objectives to meet, as a list of latencies to track. */ + public static final TypedDriverOption> METRICS_NODE_GRAPH_MESSAGES_SLO = + new TypedDriverOption<>( + DseDriverOption.METRICS_NODE_GRAPH_MESSAGES_SLO, + GenericType.listOf(GenericType.DURATION)); + /** + * Optional pre-defined percentile of node graph requests to publish, as a list of percentiles . + */ + public static final TypedDriverOption> + METRICS_NODE_GRAPH_MESSAGES_PUBLISH_PERCENTILES = + new TypedDriverOption<>( + DseDriverOption.METRICS_NODE_GRAPH_MESSAGES_PUBLISH_PERCENTILES, + GenericType.listOf(GenericType.DOUBLE)); + /** + * The number of significant decimal digits to which internal structures will maintain for graph + * requests. + */ + public static final TypedDriverOption METRICS_NODE_GRAPH_MESSAGES_DIGITS = + new TypedDriverOption<>( + DseDriverOption.METRICS_NODE_GRAPH_MESSAGES_DIGITS, GenericType.INTEGER); + /** The interval at which percentile data is refreshed for graph requests. */ + public static final TypedDriverOption METRICS_NODE_GRAPH_MESSAGES_INTERVAL = + new TypedDriverOption<>( + DseDriverOption.METRICS_NODE_GRAPH_MESSAGES_INTERVAL, GenericType.DURATION); + + /** The time after which the node level metrics will be evicted. */ + public static final TypedDriverOption METRICS_NODE_EXPIRE_AFTER = + new TypedDriverOption<>(DefaultDriverOption.METRICS_NODE_EXPIRE_AFTER, GenericType.DURATION); + + /** The classname of the desired MetricsFactory implementation. */ + public static final TypedDriverOption METRICS_FACTORY_CLASS = + new TypedDriverOption<>(DefaultDriverOption.METRICS_FACTORY_CLASS, GenericType.STRING); + + /** The classname of the desired {@code MetricIdGenerator} implementation. */ + public static final TypedDriverOption METRICS_ID_GENERATOR_CLASS = + new TypedDriverOption<>(DefaultDriverOption.METRICS_ID_GENERATOR_CLASS, GenericType.STRING); + + /** The value of the prefix to prepend to all metric names. */ + public static final TypedDriverOption METRICS_ID_GENERATOR_PREFIX = + new TypedDriverOption<>(DefaultDriverOption.METRICS_ID_GENERATOR_PREFIX, GenericType.STRING); + + /** The maximum number of nodes from remote DCs to include in query plans. */ + public static final TypedDriverOption + LOAD_BALANCING_DC_FAILOVER_MAX_NODES_PER_REMOTE_DC = + new TypedDriverOption<>( + DefaultDriverOption.LOAD_BALANCING_DC_FAILOVER_MAX_NODES_PER_REMOTE_DC, + GenericType.INTEGER); + /** Whether to consider nodes from remote DCs if the request's consistency level is local. */ + public static final TypedDriverOption + LOAD_BALANCING_DC_FAILOVER_ALLOW_FOR_LOCAL_CONSISTENCY_LEVELS = + new TypedDriverOption<>( + DefaultDriverOption.LOAD_BALANCING_DC_FAILOVER_ALLOW_FOR_LOCAL_CONSISTENCY_LEVELS, + GenericType.BOOLEAN); + + public static final TypedDriverOption ADDRESS_TRANSLATOR_ADVERTISED_HOSTNAME = + new TypedDriverOption<>( + DefaultDriverOption.ADDRESS_TRANSLATOR_ADVERTISED_HOSTNAME, GenericType.STRING); + public static final TypedDriverOption> ADDRESS_TRANSLATOR_SUBNET_ADDRESSES = + new TypedDriverOption<>( + DefaultDriverOption.ADDRESS_TRANSLATOR_SUBNET_ADDRESSES, + GenericType.mapOf(GenericType.STRING, GenericType.STRING)); + public static final TypedDriverOption ADDRESS_TRANSLATOR_DEFAULT_ADDRESS = + new TypedDriverOption<>( + DefaultDriverOption.ADDRESS_TRANSLATOR_DEFAULT_ADDRESS, GenericType.STRING); + public static final TypedDriverOption ADDRESS_TRANSLATOR_RESOLVE_ADDRESSES = + new TypedDriverOption<>( + DefaultDriverOption.ADDRESS_TRANSLATOR_RESOLVE_ADDRESSES, GenericType.BOOLEAN); + + /** + * Ordered preference list of remote dcs optionally supplied for automatic failover and included + * in query plan. This feature is enabled only when max-nodes-per-remote-dc is greater than 0. + */ + public static final TypedDriverOption> + LOAD_BALANCING_DC_FAILOVER_PREFERRED_REMOTE_DCS = + new TypedDriverOption<>( + DefaultDriverOption.LOAD_BALANCING_DC_FAILOVER_PREFERRED_REMOTE_DCS, + GenericType.listOf(String.class)); + + private static Iterable> introspectBuiltInValues() { + try { + ImmutableList.Builder> result = ImmutableList.builder(); + for (Field field : TypedDriverOption.class.getFields()) { + if ((field.getModifiers() & PUBLIC_STATIC_FINAL) == PUBLIC_STATIC_FINAL + && field.getType() == TypedDriverOption.class) { + TypedDriverOption typedOption = (TypedDriverOption) field.get(null); + result.add(typedOption); + } + } + return result.build(); + } catch (IllegalAccessException e) { + throw new IllegalStateException("Unexpected error while introspecting built-in values", e); + } + } + + private static final int PUBLIC_STATIC_FINAL = Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL; +} diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/config/package-info.java b/core/src/main/java/com/datastax/oss/driver/api/core/config/package-info.java index 6ddc5abaf62..a751d983e70 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/config/package-info.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/config/package-info.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/connection/BusyConnectionException.java b/core/src/main/java/com/datastax/oss/driver/api/core/connection/BusyConnectionException.java index 1c725715d54..8069474612a 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/connection/BusyConnectionException.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/connection/BusyConnectionException.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -25,11 +27,14 @@ * requests. * *

This might happen under heavy load. The driver will automatically try the next node in the - * query plan. Therefore the only way that the client can observe this exception is as part of a + * query plan. Therefore, the only way that the client can observe this exception is as part of a * {@link AllNodesFailedException}. */ public class BusyConnectionException extends DriverException { + // Note: the driver doesn't use this constructor anymore, it is preserved only for backward + // compatibility. + @SuppressWarnings("unused") public BusyConnectionException(int maxAvailableIds) { this( String.format( @@ -38,6 +43,10 @@ public BusyConnectionException(int maxAvailableIds) { false); } + public BusyConnectionException(String message) { + this(message, null, false); + } + private BusyConnectionException( String message, ExecutionInfo executionInfo, boolean writableStackTrace) { super(message, executionInfo, null, writableStackTrace); diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/connection/ClosedConnectionException.java b/core/src/main/java/com/datastax/oss/driver/api/core/connection/ClosedConnectionException.java index 9daee547a46..a192e2c5efc 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/connection/ClosedConnectionException.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/connection/ClosedConnectionException.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -26,8 +28,9 @@ *

For example, this can happen if the node is unresponsive and a heartbeat query failed, or if * the node was forced down. * - *

The driver will always retry these requests on the next node transparently. Therefore, the - * only way to observe this exception is as part of an {@link AllNodesFailedException}. + *

The driver will retry these requests on the next node transparently, unless the request is not + * idempotent. Therefore, this exception is usually observed as part of an {@link + * AllNodesFailedException}. */ public class ClosedConnectionException extends DriverException { diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/connection/ConnectionInitException.java b/core/src/main/java/com/datastax/oss/driver/api/core/connection/ConnectionInitException.java index 4112bdcd6f8..519624e8d5d 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/connection/ConnectionInitException.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/connection/ConnectionInitException.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -26,7 +28,7 @@ * *

The only time when this is returned directly to the client (wrapped in a {@link * AllNodesFailedException}) is at initialization. If it happens later when the driver is already - * connected, it is just logged an the connection is reattempted. + * connected, it is just logged and the connection is reattempted. */ public class ConnectionInitException extends DriverException { public ConnectionInitException(@NonNull String message, @Nullable Throwable cause) { diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/connection/CrcMismatchException.java b/core/src/main/java/com/datastax/oss/driver/api/core/connection/CrcMismatchException.java new file mode 100644 index 00000000000..d0fc8fc3b73 --- /dev/null +++ b/core/src/main/java/com/datastax/oss/driver/api/core/connection/CrcMismatchException.java @@ -0,0 +1,41 @@ +/* + * 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 com.datastax.oss.driver.api.core.connection; + +import com.datastax.oss.driver.api.core.DriverException; +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * Thrown when the checksums in a server response don't match (protocol v5 or above). + * + *

This indicates a data corruption issue, either due to a hardware issue on the client, or on + * the network between the server and the client. It is not recoverable: the driver will drop the + * connection. + */ +public class CrcMismatchException extends DriverException { + + public CrcMismatchException(@NonNull String message) { + super(message, null, null, true); + } + + @NonNull + @Override + public DriverException copy() { + return new CrcMismatchException(getMessage()); + } +} diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/connection/FrameTooLongException.java b/core/src/main/java/com/datastax/oss/driver/api/core/connection/FrameTooLongException.java index e84504d089f..9954aefb3d4 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/connection/FrameTooLongException.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/connection/FrameTooLongException.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/connection/HeartbeatException.java b/core/src/main/java/com/datastax/oss/driver/api/core/connection/HeartbeatException.java index 183f7c5366e..60c3d60a69d 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/connection/HeartbeatException.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/connection/HeartbeatException.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -28,8 +30,8 @@ * *

Heartbeat queries are sent automatically on idle connections, to ensure that they are still * alive. If a heartbeat query fails, the connection is closed, and all pending queries are aborted. - * The exception will be passed to {@link RetryPolicy#onRequestAborted(Request, Throwable, int)}, - * which decides what to do next (the default policy retries the query on the next node). + * The exception will be passed to {@link RetryPolicy#onRequestAbortedVerdict(Request, Throwable, + * int)}, which decides what to do next (the default policy retries the query on the next node). */ public class HeartbeatException extends DriverException { diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/connection/ReconnectionPolicy.java b/core/src/main/java/com/datastax/oss/driver/api/core/connection/ReconnectionPolicy.java index 083e83950c6..9f81843c9c0 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/connection/ReconnectionPolicy.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/connection/ReconnectionPolicy.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/connection/package-info.java b/core/src/main/java/com/datastax/oss/driver/api/core/connection/package-info.java index ade4f228669..737f985ad1d 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/connection/package-info.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/connection/package-info.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/context/DriverContext.java b/core/src/main/java/com/datastax/oss/driver/api/core/context/DriverContext.java index b4efb691494..6f0afd3df8a 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/context/DriverContext.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/context/DriverContext.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -31,6 +33,7 @@ import com.datastax.oss.driver.api.core.specex.SpeculativeExecutionPolicy; import com.datastax.oss.driver.api.core.ssl.SslEngineFactory; import com.datastax.oss.driver.api.core.time.TimestampGenerator; +import com.datastax.oss.driver.api.core.tracker.RequestIdGenerator; import com.datastax.oss.driver.api.core.tracker.RequestTracker; import edu.umd.cs.findbugs.annotations.NonNull; import java.util.Map; @@ -137,6 +140,10 @@ default SpeculativeExecutionPolicy getSpeculativeExecutionPolicy(@NonNull String @NonNull RequestTracker getRequestTracker(); + /** @return The driver's request ID generator; never {@code null}. */ + @NonNull + Optional getRequestIdGenerator(); + /** @return The driver's request throttler; never {@code null}. */ @NonNull RequestThrottler getRequestThrottler(); diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/cql/AsyncCqlSession.java b/core/src/main/java/com/datastax/oss/driver/api/core/cql/AsyncCqlSession.java new file mode 100644 index 00000000000..7b56bd61a09 --- /dev/null +++ b/core/src/main/java/com/datastax/oss/driver/api/core/cql/AsyncCqlSession.java @@ -0,0 +1,160 @@ +/* + * 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 com.datastax.oss.driver.api.core.cql; + +import com.datastax.oss.driver.api.core.session.Session; +import com.datastax.oss.driver.internal.core.cql.DefaultPrepareRequest; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.CompletionStage; + +/** + * A session that offers user-friendly methods to execute CQL requests asynchronously. + * + * @since 4.4.0 + */ +public interface AsyncCqlSession extends Session { + + /** + * Executes a CQL statement asynchronously (the call returns as soon as the statement was sent, + * generally before the result is available). + * + * @param statement the CQL query to execute (that can be any {@code Statement}). + * @return a {@code CompletionStage} that, once complete, will produce the async result set. + */ + @NonNull + default CompletionStage executeAsync(@NonNull Statement statement) { + return Objects.requireNonNull( + execute(statement, Statement.ASYNC), "The CQL processor should never return a null result"); + } + + /** + * Executes a CQL statement asynchronously (the call returns as soon as the statement was sent, + * generally before the result is available). + * + *

This is an alias for {@link #executeAsync(Statement)} + * executeAsync(SimpleStatement.newInstance(query))}. + * + * @param query the CQL query to execute. + * @return a {@code CompletionStage} that, once complete, will produce the async result set. + * @see SimpleStatement#newInstance(String) + */ + @NonNull + default CompletionStage executeAsync(@NonNull String query) { + return executeAsync(SimpleStatement.newInstance(query)); + } + + /** + * Executes a CQL statement asynchronously (the call returns as soon as the statement was sent, + * generally before the result is available). + * + *

This is an alias for {@link #executeAsync(Statement)} + * executeAsync(SimpleStatement.newInstance(query, values))}. + * + * @param query the CQL query to execute. + * @param values the values for placeholders in the query string. Individual values can be {@code + * null}, but the vararg array itself can't. + * @return a {@code CompletionStage} that, once complete, will produce the async result set. + * @see SimpleStatement#newInstance(String, Object...) + */ + @NonNull + default CompletionStage executeAsync( + @NonNull String query, @NonNull Object... values) { + return executeAsync(SimpleStatement.newInstance(query, values)); + } + + /** + * Executes a CQL statement asynchronously (the call returns as soon as the statement was sent, + * generally before the result is available). + * + *

This is an alias for {@link #executeAsync(Statement)} + * executeAsync(SimpleStatement.newInstance(query, values))}. + * + * @param query the CQL query to execute. + * @param values the values for named placeholders in the query string. Individual values can be + * {@code null}, but the map itself can't. + * @return a {@code CompletionStage} that, once complete, will produce the async result set. + * @see SimpleStatement#newInstance(String, Map) + */ + @NonNull + default CompletionStage executeAsync( + @NonNull String query, @NonNull Map values) { + return executeAsync(SimpleStatement.newInstance(query, values)); + } + + /** + * Prepares a CQL statement asynchronously (the call returns as soon as the prepare query was + * sent, generally before the statement is prepared). + * + *

Note that the bound statements created from the resulting prepared statement will inherit + * some of the attributes of {@code query}; see {@link SyncCqlSession#prepare(SimpleStatement)} + * for more details. + * + *

The result of this method is cached (see {@link SyncCqlSession#prepare(SimpleStatement)} for + * more explanations). + * + * @param statement the CQL query to prepare (that can be any {@code SimpleStatement}). + * @return a {@code CompletionStage} that, once complete, will produce the prepared statement. + */ + @NonNull + default CompletionStage prepareAsync(@NonNull SimpleStatement statement) { + return Objects.requireNonNull( + execute(new DefaultPrepareRequest(statement), PrepareRequest.ASYNC), + "The CQL prepare processor should never return a null result"); + } + + /** + * Prepares a CQL statement asynchronously (the call returns as soon as the prepare query was + * sent, generally before the statement is prepared). + * + *

The result of this method is cached (see {@link SyncCqlSession#prepare(SimpleStatement)} for + * more explanations). + * + * @param query the CQL query string to prepare. + * @return a {@code CompletionStage} that, once complete, will produce the prepared statement. + */ + @NonNull + default CompletionStage prepareAsync(@NonNull String query) { + return Objects.requireNonNull( + execute(new DefaultPrepareRequest(query), PrepareRequest.ASYNC), + "The CQL prepare processor should never return a null result"); + } + + /** + * Prepares a CQL statement asynchronously (the call returns as soon as the prepare query was + * sent, generally before the statement is prepared). + * + *

This variant is exposed in case you use an ad hoc {@link PrepareRequest} implementation to + * customize how attributes are propagated when you prepare a {@link SimpleStatement} (see {@link + * SyncCqlSession#prepare(SimpleStatement)} for more explanations). Otherwise, you should rarely + * have to deal with {@link PrepareRequest} directly. + * + *

The result of this method is cached (see {@link SyncCqlSession#prepare(SimpleStatement)} for + * more explanations). + * + * @param request the {@code PrepareRequest} to prepare. + * @return a {@code CompletionStage} that, once complete, will produce the prepared statement. + */ + @NonNull + default CompletionStage prepareAsync(PrepareRequest request) { + return Objects.requireNonNull( + execute(request, PrepareRequest.ASYNC), + "The CQL prepare processor should never return a null result"); + } +} diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/cql/AsyncResultSet.java b/core/src/main/java/com/datastax/oss/driver/api/core/cql/AsyncResultSet.java index a21c0ee8cd6..05a292ccbd0 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/cql/AsyncResultSet.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/cql/AsyncResultSet.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/cql/BatchStatement.java b/core/src/main/java/com/datastax/oss/driver/api/core/cql/BatchStatement.java index 1219dad4475..9deb33c6007 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/cql/BatchStatement.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/cql/BatchStatement.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -24,6 +26,7 @@ import com.datastax.oss.driver.internal.core.util.Sizes; import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableList; import com.datastax.oss.protocol.internal.PrimitiveSizes; +import edu.umd.cs.findbugs.annotations.CheckReturnValue; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import java.util.ArrayList; @@ -57,15 +60,16 @@ static BatchStatement newInstance(@NonNull BatchType batchType) { null, null, Collections.emptyMap(), + null, false, - false, - Long.MIN_VALUE, + Statement.NO_DEFAULT_TIMESTAMP, null, Integer.MIN_VALUE, null, null, null, - null); + null, + Statement.NO_NOW_IN_SECONDS); } /** @@ -87,15 +91,16 @@ static BatchStatement newInstance( null, null, Collections.emptyMap(), + null, false, - false, - Long.MIN_VALUE, + Statement.NO_DEFAULT_TIMESTAMP, null, Integer.MIN_VALUE, null, null, null, - null); + null, + Statement.NO_NOW_IN_SECONDS); } /** @@ -117,18 +122,23 @@ static BatchStatement newInstance( null, null, Collections.emptyMap(), + null, false, - false, - Long.MIN_VALUE, + Statement.NO_DEFAULT_TIMESTAMP, null, Integer.MIN_VALUE, null, null, null, - null); + null, + Statement.NO_NOW_IN_SECONDS); } - /** Returns a builder to create an instance of the default implementation. */ + /** + * Returns a builder to create an instance of the default implementation. + * + *

Note that this builder is mutable and not thread-safe. + */ @NonNull static BatchStatementBuilder builder(@NonNull BatchType batchType) { return new BatchStatementBuilder(batchType); @@ -137,6 +147,8 @@ static BatchStatementBuilder builder(@NonNull BatchType batchType) { /** * Returns a builder to create an instance of the default implementation, copying the fields of * the given statement. + * + *

Note that this builder is mutable and not thread-safe. */ @NonNull static BatchStatementBuilder builder(@NonNull BatchStatement template) { @@ -153,6 +165,7 @@ static BatchStatementBuilder builder(@NonNull BatchStatement template) { * method. However custom implementations may choose to be mutable and return the same instance. */ @NonNull + @CheckReturnValue BatchStatement setBatchType(@NonNull BatchType newBatchType); /** @@ -169,6 +182,7 @@ static BatchStatementBuilder builder(@NonNull BatchStatement template) { * @see Request#getKeyspace() */ @NonNull + @CheckReturnValue BatchStatement setKeyspace(@Nullable CqlIdentifier newKeyspace); /** @@ -176,6 +190,7 @@ static BatchStatementBuilder builder(@NonNull BatchStatement template) { * setKeyspace(CqlIdentifier.fromCql(newKeyspaceName))}. */ @NonNull + @CheckReturnValue default BatchStatement setKeyspace(@NonNull String newKeyspaceName) { return setKeyspace(CqlIdentifier.fromCql(newKeyspaceName)); } @@ -190,6 +205,7 @@ default BatchStatement setKeyspace(@NonNull String newKeyspaceName) { * method. However custom implementations may choose to be mutable and return the same instance. */ @NonNull + @CheckReturnValue BatchStatement add(@NonNull BatchableStatement statement); /** @@ -202,10 +218,12 @@ default BatchStatement setKeyspace(@NonNull String newKeyspaceName) { * method. However custom implementations may choose to be mutable and return the same instance. */ @NonNull + @CheckReturnValue BatchStatement addAll(@NonNull Iterable> statements); /** @see #addAll(Iterable) */ @NonNull + @CheckReturnValue default BatchStatement addAll(@NonNull BatchableStatement... statements) { return addAll(Arrays.asList(statements)); } @@ -220,6 +238,7 @@ default BatchStatement addAll(@NonNull BatchableStatement... statements) { * method. However custom implementations may choose to be mutable and return the same instance. */ @NonNull + @CheckReturnValue BatchStatement clear(); @Override @@ -251,7 +270,7 @@ default int computeSizeInBytes(@NonNull DriverContext context) { // timestamp if (!(context.getTimestampGenerator() instanceof ServerSideTimestampGenerator) - || getQueryTimestamp() != Long.MIN_VALUE) { + || getQueryTimestamp() != Statement.NO_DEFAULT_TIMESTAMP) { size += PrimitiveSizes.LONG; } diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/cql/BatchStatementBuilder.java b/core/src/main/java/com/datastax/oss/driver/api/core/cql/BatchStatementBuilder.java index de3283b4a36..a8e2b8ab659 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/cql/BatchStatementBuilder.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/cql/BatchStatementBuilder.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -24,6 +26,11 @@ import java.util.Arrays; import net.jcip.annotations.NotThreadSafe; +/** + * A builder to create a batch statement. + * + *

This class is mutable and not thread-safe. + */ @NotThreadSafe public class BatchStatementBuilder extends StatementBuilder { @@ -147,7 +154,8 @@ public BatchStatement build() { consistencyLevel, serialConsistencyLevel, timeout, - node); + node, + nowInSeconds); } public int getStatementsCount() { diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/cql/BatchType.java b/core/src/main/java/com/datastax/oss/driver/api/core/cql/BatchType.java index f81d6c326bf..6b0a7f09688 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/cql/BatchType.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/cql/BatchType.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -24,6 +26,10 @@ */ public interface BatchType { + BatchType LOGGED = DefaultBatchType.LOGGED; + BatchType UNLOGGED = DefaultBatchType.UNLOGGED; + BatchType COUNTER = DefaultBatchType.COUNTER; + /** The numerical value that the batch type is encoded to. */ byte getProtocolCode(); diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/cql/BatchableStatement.java b/core/src/main/java/com/datastax/oss/driver/api/core/cql/BatchableStatement.java index 5fb50fc5348..a25f625bae9 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/cql/BatchableStatement.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/cql/BatchableStatement.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/cql/Bindable.java b/core/src/main/java/com/datastax/oss/driver/api/core/cql/Bindable.java index dc9577ae23e..64f0f22a051 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/cql/Bindable.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/cql/Bindable.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/cql/BoundStatement.java b/core/src/main/java/com/datastax/oss/driver/api/core/cql/BoundStatement.java index c8d17189721..bd7c142907f 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/cql/BoundStatement.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/cql/BoundStatement.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -66,11 +68,11 @@ default int computeSizeInBytes(@NonNull DriverContext context) { // - timestamp // prepared ID - size += PrimitiveSizes.sizeOfShortBytes(getPreparedStatement().getId().array()); + size += PrimitiveSizes.sizeOfShortBytes(getPreparedStatement().getId()); // result metadata ID if (getPreparedStatement().getResultMetadataId() != null) { - size += PrimitiveSizes.sizeOfShortBytes(getPreparedStatement().getResultMetadataId().array()); + size += PrimitiveSizes.sizeOfShortBytes(getPreparedStatement().getResultMetadataId()); } // parameters (always sent as positional values for bound statements) @@ -86,7 +88,7 @@ default int computeSizeInBytes(@NonNull DriverContext context) { // timestamp if (!(context.getTimestampGenerator() instanceof ServerSideTimestampGenerator) - || getQueryTimestamp() != Long.MIN_VALUE) { + || getQueryTimestamp() != Statement.NO_DEFAULT_TIMESTAMP) { size += PrimitiveSizes.LONG; } diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/cql/BoundStatementBuilder.java b/core/src/main/java/com/datastax/oss/driver/api/core/cql/BoundStatementBuilder.java index 579dd8e399b..7e8f8723e1b 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/cql/BoundStatementBuilder.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/cql/BoundStatementBuilder.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -27,9 +29,15 @@ import edu.umd.cs.findbugs.annotations.Nullable; import java.nio.ByteBuffer; import java.time.Duration; +import java.util.List; import java.util.Map; import net.jcip.annotations.NotThreadSafe; +/** + * A builder to create a bound statement. + * + *

This class is mutable and not thread-safe. + */ @NotThreadSafe public class BoundStatementBuilder extends StatementBuilder implements Bindable { @@ -93,6 +101,22 @@ public BoundStatementBuilder(@NonNull BoundStatement template) { this.node = template.getNode(); } + /** The prepared statement that was used to create this statement. */ + @NonNull + public PreparedStatement getPreparedStatement() { + return preparedStatement; + } + + @NonNull + @Override + public List allIndicesOf(@NonNull CqlIdentifier id) { + List indices = variableDefinitions.allIndicesOf(id); + if (indices.isEmpty()) { + throw new IllegalArgumentException(id + " is not a variable in this bound statement"); + } + return indices; + } + @Override public int firstIndexOf(@NonNull CqlIdentifier id) { int indexOf = variableDefinitions.firstIndexOf(id); @@ -102,6 +126,16 @@ public int firstIndexOf(@NonNull CqlIdentifier id) { return indexOf; } + @NonNull + @Override + public List allIndicesOf(@NonNull String name) { + List indices = variableDefinitions.allIndicesOf(name); + if (indices.isEmpty()) { + throw new IllegalArgumentException(name + " is not a variable in this bound statement"); + } + return indices; + } + @Override public int firstIndexOf(@NonNull String name) { int indexOf = variableDefinitions.firstIndexOf(name); @@ -169,6 +203,7 @@ public BoundStatement build() { timeout, codecRegistry, protocolVersion, - node); + node, + nowInSeconds); } } diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/cql/ColumnDefinition.java b/core/src/main/java/com/datastax/oss/driver/api/core/cql/ColumnDefinition.java index 5bdee0410ad..cb48f058be4 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/cql/ColumnDefinition.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/cql/ColumnDefinition.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/cql/ColumnDefinitions.java b/core/src/main/java/com/datastax/oss/driver/api/core/cql/ColumnDefinitions.java index 15b206c0a6f..7a775064317 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/cql/ColumnDefinitions.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/cql/ColumnDefinitions.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -18,7 +20,10 @@ import com.datastax.oss.driver.api.core.CqlIdentifier; import com.datastax.oss.driver.api.core.data.AccessibleByName; import com.datastax.oss.driver.api.core.detach.Detachable; +import com.datastax.oss.driver.internal.core.util.Loggers; import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.Collections; +import java.util.List; /** * Metadata about a set of CQL columns. @@ -97,22 +102,61 @@ default ColumnDefinition get(@NonNull CqlIdentifier name) { /** Whether there is a definition using the given CQL identifier. */ boolean contains(@NonNull CqlIdentifier id); + /** + * Returns the indices of all columns that use the given name. + * + *

Because raw strings are ambiguous with regard to case-sensitivity, the argument will be + * interpreted according to the rules described in {@link AccessibleByName}. + * + * @return the indices, or an empty list if no column uses this name. + * @apiNote the default implementation only exists for backward compatibility. It wraps the result + * of {@link #firstIndexOf(String)} in a singleton list, which is not entirely correct, as it + * will only return the first occurrence. Therefore it also logs a warning. + *

Implementors should always override this method (all built-in driver implementations + * do). + */ + @NonNull + default List allIndicesOf(@NonNull String name) { + Loggers.COLUMN_DEFINITIONS.warn( + "{} should override allIndicesOf(String), the default implementation is a " + + "workaround for backward compatibility, it only returns the first occurrence", + getClass().getName()); + return Collections.singletonList(firstIndexOf(name)); + } + /** * Returns the index of the first column that uses the given name. * *

Because raw strings are ambiguous with regard to case-sensitivity, the argument will be * interpreted according to the rules described in {@link AccessibleByName}. * - *

Also, note that if multiple columns use the same name, there is no way to find the index for - * the next occurrences. One way to avoid this is to use aliases in your CQL queries. + * @return the index, or -1 if no column uses this name. */ int firstIndexOf(@NonNull String name); + /** + * Returns the indices of all columns that use the given identifier. + * + * @return the indices, or an empty list if no column uses this identifier. + * @apiNote the default implementation only exists for backward compatibility. It wraps the result + * of {@link #firstIndexOf(CqlIdentifier)} in a singleton list, which is not entirely correct, + * as it will only return the first occurrence. Therefore it also logs a warning. + *

Implementors should always override this method (all built-in driver implementations + * do). + */ + @NonNull + default List allIndicesOf(@NonNull CqlIdentifier id) { + Loggers.COLUMN_DEFINITIONS.warn( + "{} should override allIndicesOf(CqlIdentifier), the default implementation is a " + + "workaround for backward compatibility, it only returns the first occurrence", + getClass().getName()); + return Collections.singletonList(firstIndexOf(id)); + } + /** * Returns the index of the first column that uses the given identifier. * - *

Note that if multiple columns use the same identifier, there is no way to find the index for - * the next occurrences. One way to avoid this is to use aliases in your CQL queries. + * @return the index, or -1 if no column uses this identifier. */ int firstIndexOf(@NonNull CqlIdentifier id); } diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/cql/DefaultBatchType.java b/core/src/main/java/com/datastax/oss/driver/api/core/cql/DefaultBatchType.java index f941d48906d..f699438df59 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/cql/DefaultBatchType.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/cql/DefaultBatchType.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -38,6 +40,9 @@ public enum DefaultBatchType implements BatchType { */ COUNTER(ProtocolConstants.BatchType.COUNTER), ; + // Note that, for the sake of convenience, we also expose shortcuts to these constants on the + // BatchType interface. If you add a new enum constant, remember to update the interface as + // well. private final byte code; diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/cql/ExecutionInfo.java b/core/src/main/java/com/datastax/oss/driver/api/core/cql/ExecutionInfo.java index 5187966b720..40cfca827d1 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/cql/ExecutionInfo.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/cql/ExecutionInfo.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -19,11 +21,14 @@ import com.datastax.oss.driver.api.core.DriverException; import com.datastax.oss.driver.api.core.RequestThrottlingException; import com.datastax.oss.driver.api.core.config.DefaultDriverOption; +import com.datastax.oss.driver.api.core.detach.AttachmentPoint; import com.datastax.oss.driver.api.core.metadata.Node; import com.datastax.oss.driver.api.core.retry.RetryDecision; import com.datastax.oss.driver.api.core.servererrors.CoordinatorException; +import com.datastax.oss.driver.api.core.session.Request; import com.datastax.oss.driver.api.core.session.Session; import com.datastax.oss.driver.api.core.specex.SpeculativeExecutionPolicy; +import com.datastax.oss.driver.internal.core.cql.DefaultPagingState; import com.datastax.oss.driver.internal.core.util.concurrent.BlockingOperation; import com.datastax.oss.driver.internal.core.util.concurrent.CompletableFutures; import edu.umd.cs.findbugs.annotations.NonNull; @@ -45,8 +50,20 @@ */ public interface ExecutionInfo { - /** The statement that was executed. */ + /** @return The {@link Request} that was executed. */ @NonNull + default Request getRequest() { + return getStatement(); + } + + /** + * @return The {@link Request} that was executed, if it can be cast to {@link Statement}. + * @deprecated Use {@link #getRequest()} instead. + * @throws ClassCastException If the request that was executed cannot be cast to {@link + * Statement}. + */ + @NonNull + @Deprecated Statement getStatement(); /** @@ -97,16 +114,45 @@ public interface ExecutionInfo { List> getErrors(); /** - * The paging state of the query. + * The paging state of the query, in its raw form. * *

This represents the next page to be fetched if this query has multiple page of results. It * can be saved and reused later on the same statement. * + *

Note that this is the equivalent of driver 3's {@code getPagingStateUnsafe()}. If you're + * looking for the method that returns a {@link PagingState}, use {@link #getSafePagingState()}. + * * @return the paging state, or {@code null} if there is no next page. */ @Nullable ByteBuffer getPagingState(); + /** + * The paging state of the query, in a safe wrapper that checks if it's reused on the right + * statement. + * + *

This represents the next page to be fetched if this query has multiple page of results. It + * can be saved and reused later on the same statement. + * + * @return the paging state, or {@code null} if there is no next page. + */ + @Nullable + default PagingState getSafePagingState() { + // Default implementation for backward compatibility, but we override it in the concrete class, + // because it knows the attachment point. + ByteBuffer rawPagingState = getPagingState(); + if (rawPagingState == null) { + return null; + } else { + Request request = getRequest(); + if (!(request instanceof Statement)) { + throw new IllegalStateException("Only statements should have a paging state"); + } + Statement statement = (Statement) request; + return new DefaultPagingState(rawPagingState, statement, AttachmentPoint.NONE); + } + } + /** * The server-side warnings for this query, if any (otherwise the list will be empty). * diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/cql/PagingState.java b/core/src/main/java/com/datastax/oss/driver/api/core/cql/PagingState.java new file mode 100644 index 00000000000..b9042f99841 --- /dev/null +++ b/core/src/main/java/com/datastax/oss/driver/api/core/cql/PagingState.java @@ -0,0 +1,82 @@ +/* + * 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 com.datastax.oss.driver.api.core.cql; + +import com.datastax.oss.driver.api.core.session.Session; +import com.datastax.oss.driver.internal.core.cql.DefaultPagingState; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.nio.ByteBuffer; + +/** + * A safe wrapper around the paging state of a query. + * + *

This class performs additional checks to fail fast if the paging state is not reused on the + * same query, and it provides utility methods for conversion to/from strings and byte arrays. + * + *

The serialized form returned by {@link #toBytes()} and {@link Object#toString()} is an opaque + * sequence of bytes. Note however that it is not cryptographically secure: the contents are + * not encrypted and the checks are performed with a simple MD5 checksum. If you need stronger + * guarantees, you should build your own wrapper around {@link ExecutionInfo#getPagingState()}. + */ +public interface PagingState { + + /** Parses an instance from a string previously generated with {@code toString()}. */ + @NonNull + static PagingState fromString(@NonNull String string) { + return DefaultPagingState.fromString(string); + } + + /** Parses an instance from a byte array previously generated with {@link #toBytes()}. */ + @NonNull + static PagingState fromBytes(byte[] bytes) { + return DefaultPagingState.fromBytes(bytes); + } + + /** Returns a representation of this object as a byte array. */ + byte[] toBytes(); + + /** + * Checks if this paging state can be safely reused for the given statement. Specifically, the + * query string and any bound values must match. + * + *

Note that, if {@code statement} is a {@link SimpleStatement} with bound values, those values + * must be encoded in order to perform the check. This method uses the default codec registry and + * default protocol version. This might fail if you use custom codecs; in that case, use {@link + * #matches(Statement, Session)} instead. + * + *

If {@code statement} is a {@link BoundStatement}, it is always safe to call this method. + */ + default boolean matches(@NonNull Statement statement) { + return matches(statement, null); + } + + /** + * Alternative to {@link #matches(Statement)} that specifies the session the statement will be + * executed with. You only need this for simple statements, and if you use custom codecs. + * Bound statements already know which session they are attached to. + */ + boolean matches(@NonNull Statement statement, @Nullable Session session); + + /** + * Returns the underlying "unsafe" paging state (the equivalent of {@link + * ExecutionInfo#getPagingState()}). + */ + @NonNull + ByteBuffer getRawPagingState(); +} diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/cql/PrepareRequest.java b/core/src/main/java/com/datastax/oss/driver/api/core/cql/PrepareRequest.java index 3e7308ccd4f..eb04f26c046 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/cql/PrepareRequest.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/cql/PrepareRequest.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/cql/PreparedStatement.java b/core/src/main/java/com/datastax/oss/driver/api/core/cql/PreparedStatement.java index b9f9a0fdccf..7828f9f809c 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/cql/PreparedStatement.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/cql/PreparedStatement.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -134,6 +136,8 @@ void setResultMetadata( /** * Returns a builder to construct an executable statement. * + *

Note that this builder is mutable and not thread-safe. + * * @see #bind(Object...) */ @NonNull diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/cql/QueryTrace.java b/core/src/main/java/com/datastax/oss/driver/api/core/cql/QueryTrace.java index 4af0648ce4c..37ebb85c0db 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/cql/QueryTrace.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/cql/QueryTrace.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -17,6 +19,7 @@ import edu.umd.cs.findbugs.annotations.NonNull; import java.net.InetAddress; +import java.net.InetSocketAddress; import java.util.List; import java.util.Map; import java.util.UUID; @@ -39,10 +42,29 @@ public interface QueryTrace { /** The server-side duration of the query in microseconds. */ int getDurationMicros(); - /** The IP of the node that coordinated the query. */ + /** + * @deprecated returns the coordinator IP, but {@link #getCoordinatorAddress()} should be + * preferred, since C* 4.0 and above now returns the port was well. + */ @NonNull + @Deprecated InetAddress getCoordinator(); + /** + * The IP and port of the node that coordinated the query. Prior to C* 4.0 the port is not set and + * will default to 0. + * + *

This method's default implementation returns {@link #getCoordinator()} with the port set to + * 0. The only reason it exists is to preserve binary compatibility. Internally, the driver + * overrides it to set the correct port. + * + * @since 4.6.0 + */ + @NonNull + default InetSocketAddress getCoordinatorAddress() { + return new InetSocketAddress(getCoordinator(), 0); + } + /** The parameters attached to this trace. */ @NonNull Map getParameters(); diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/cql/ResultSet.java b/core/src/main/java/com/datastax/oss/driver/api/core/cql/ResultSet.java index d4383476ca3..54f786b2068 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/cql/ResultSet.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/cql/ResultSet.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/cql/Row.java b/core/src/main/java/com/datastax/oss/driver/api/core/cql/Row.java index 9a2e88e27e8..5eab449b057 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/cql/Row.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/cql/Row.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -19,6 +21,7 @@ import com.datastax.oss.driver.api.core.data.GettableByIndex; import com.datastax.oss.driver.api.core.data.GettableByName; import com.datastax.oss.driver.api.core.detach.Detachable; +import com.datastax.oss.driver.api.core.type.codec.TypeCodec; import edu.umd.cs.findbugs.annotations.NonNull; /** @@ -33,4 +36,55 @@ public interface Row extends GettableByIndex, GettableByName, GettableById, Deta /** @return the column definitions contained in this result set. */ @NonNull ColumnDefinitions getColumnDefinitions(); + + /** + * Returns a string representation of the contents of this row. + * + *

This produces a comma-separated list enclosed in square brackets. Each column is represented + * by its name, followed by a column and the value as a CQL literal. For example: + * + *

+   * [id:1, name:'test']
+   * 
+ * + * Notes: + * + *
    + *
  • This method does not sanitize its output in any way. In particular, no effort is made to + * limit output size: all columns are included, and large strings or blobs will be appended + * as-is. + *
  • Be mindful of how you expose the result. For example, in high-security environments, it + * might be undesirable to leak data in application logs. + *
+ */ + @NonNull + default String getFormattedContents() { + StringBuilder result = new StringBuilder("["); + ColumnDefinitions definitions = getColumnDefinitions(); + for (int i = 0; i < definitions.size(); i++) { + if (i > 0) { + result.append(", "); + } + ColumnDefinition definition = definitions.get(i); + String name = definition.getName().asCql(true); + TypeCodec codec = codecRegistry().codecFor(definition.getType()); + Object value = codec.decode(getBytesUnsafe(i), protocolVersion()); + result.append(name).append(':').append(codec.format(value)); + } + return result.append("]").toString(); + } + + /** + * Returns an abstract representation of this object, that may not include the row's + * contents. + * + *

The driver's built-in {@link Row} implementation returns the default format of {@link + * Object#toString()}: the class name, followed by the at-sign and the hash code of the object. + * + *

Omitting the contents was a deliberate choice, because we feel it would make it too easy to + * accidentally leak data (e.g. in application logs). If you want the contents, use {@link + * #getFormattedContents()}. + */ + @Override + String toString(); } diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/cql/SimpleStatement.java b/core/src/main/java/com/datastax/oss/driver/api/core/cql/SimpleStatement.java index f15efc7df7c..ef04cd14a5b 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/cql/SimpleStatement.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/cql/SimpleStatement.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -26,6 +28,7 @@ import com.datastax.oss.protocol.internal.PrimitiveSizes; import com.datastax.oss.protocol.internal.util.collection.NullAllowingImmutableList; import com.datastax.oss.protocol.internal.util.collection.NullAllowingImmutableMap; +import edu.umd.cs.findbugs.annotations.CheckReturnValue; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import java.util.List; @@ -74,13 +77,14 @@ static SimpleStatement newInstance(@NonNull String cqlQuery) { NullAllowingImmutableMap.of(), null, false, - Long.MIN_VALUE, + Statement.NO_DEFAULT_TIMESTAMP, null, Integer.MIN_VALUE, null, null, null, - null); + null, + Statement.NO_NOW_IN_SECONDS); } /** @@ -107,13 +111,14 @@ static SimpleStatement newInstance( NullAllowingImmutableMap.of(), null, false, - Long.MIN_VALUE, + Statement.NO_DEFAULT_TIMESTAMP, null, Integer.MIN_VALUE, null, null, null, - null); + null, + Statement.NO_NOW_IN_SECONDS); } /** @@ -137,16 +142,21 @@ static SimpleStatement newInstance( NullAllowingImmutableMap.of(), null, false, - Long.MIN_VALUE, + Statement.NO_DEFAULT_TIMESTAMP, null, Integer.MIN_VALUE, null, null, null, - null); + null, + Statement.NO_NOW_IN_SECONDS); } - /** Returns a builder to create an instance of the default implementation. */ + /** + * Returns a builder to create an instance of the default implementation. + * + *

Note that this builder is mutable and not thread-safe. + */ @NonNull static SimpleStatementBuilder builder(@NonNull String query) { return new SimpleStatementBuilder(query); @@ -155,6 +165,8 @@ static SimpleStatementBuilder builder(@NonNull String query) { /** * Returns a builder to create an instance of the default implementation, copying the fields of * the given statement. + * + *

Note that this builder is mutable and not thread-safe. */ @NonNull static SimpleStatementBuilder builder(@NonNull SimpleStatement template) { @@ -186,6 +198,7 @@ static SimpleStatementBuilder builder(@NonNull SimpleStatement template) { * @see #setNamedValuesWithIds(Map) */ @NonNull + @CheckReturnValue SimpleStatement setQuery(@NonNull String newQuery); /** @@ -198,6 +211,7 @@ static SimpleStatementBuilder builder(@NonNull SimpleStatement template) { * @see Request#getKeyspace() */ @NonNull + @CheckReturnValue SimpleStatement setKeyspace(@Nullable CqlIdentifier newKeyspace); /** @@ -205,6 +219,7 @@ static SimpleStatementBuilder builder(@NonNull SimpleStatement template) { * setKeyspace(CqlIdentifier.fromCql(newKeyspaceName))}. */ @NonNull + @CheckReturnValue default SimpleStatement setKeyspace(@NonNull String newKeyspaceName) { return setKeyspace(CqlIdentifier.fromCql(newKeyspaceName)); } @@ -225,6 +240,7 @@ default SimpleStatement setKeyspace(@NonNull String newKeyspaceName) { * @see #setQuery(String) */ @NonNull + @CheckReturnValue SimpleStatement setPositionalValues(@NonNull List newPositionalValues); @NonNull @@ -245,6 +261,7 @@ default SimpleStatement setKeyspace(@NonNull String newKeyspaceName) { * @see #setQuery(String) */ @NonNull + @CheckReturnValue SimpleStatement setNamedValuesWithIds(@NonNull Map newNamedValues); /** @@ -252,6 +269,7 @@ default SimpleStatement setKeyspace(@NonNull String newKeyspaceName) { * converted on the fly with {@link CqlIdentifier#fromCql(String)}. */ @NonNull + @CheckReturnValue default SimpleStatement setNamedValues(@NonNull Map newNamedValues) { return setNamedValuesWithIds(DefaultSimpleStatement.wrapKeys(newNamedValues)); } @@ -291,7 +309,7 @@ default int computeSizeInBytes(@NonNull DriverContext context) { // timestamp if (!(context.getTimestampGenerator() instanceof ServerSideTimestampGenerator) - || getQueryTimestamp() != Long.MIN_VALUE) { + || getQueryTimestamp() != Statement.NO_DEFAULT_TIMESTAMP) { size += PrimitiveSizes.LONG; } diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/cql/SimpleStatementBuilder.java b/core/src/main/java/com/datastax/oss/driver/api/core/cql/SimpleStatementBuilder.java index 4a1a9e32233..1ac910ff6a7 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/cql/SimpleStatementBuilder.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/cql/SimpleStatementBuilder.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -26,6 +28,11 @@ import java.util.Map; import net.jcip.annotations.NotThreadSafe; +/** + * A builder to create a simple statement. + * + *

This class is mutable and not thread-safe. + */ @NotThreadSafe public class SimpleStatementBuilder extends StatementBuilder { @@ -177,6 +184,7 @@ public SimpleStatement build() { consistencyLevel, serialConsistencyLevel, timeout, - node); + node, + nowInSeconds); } } diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/cql/Statement.java b/core/src/main/java/com/datastax/oss/driver/api/core/cql/Statement.java index c06b24e1982..d70c56686c5 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/cql/Statement.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/cql/Statement.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -31,6 +33,7 @@ import com.datastax.oss.driver.api.core.time.TimestampGenerator; import com.datastax.oss.driver.api.core.type.reflect.GenericType; import com.datastax.oss.driver.internal.core.util.RoutingKey; +import com.datastax.oss.protocol.internal.request.query.QueryOptions; import edu.umd.cs.findbugs.annotations.CheckReturnValue; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; @@ -67,11 +70,25 @@ public interface Statement> extends Request { GenericType> ASYNC = new GenericType>() {}; + /** + * A special value for {@link #getQueryTimestamp()} that means "no value". + * + *

It is equal to {@link Long#MIN_VALUE}. + */ + long NO_DEFAULT_TIMESTAMP = QueryOptions.NO_DEFAULT_TIMESTAMP; + + /** + * A special value for {@link #getNowInSeconds()} that means "no value". + * + *

It is equal to {@link Integer#MIN_VALUE}. + */ + int NO_NOW_IN_SECONDS = QueryOptions.NO_NOW_IN_SECONDS; + /** * Sets the name of the execution profile that will be used for this statement. * - *

For all the driver's built-in implementations, this method has no effect if {@link - * #setExecutionProfile(DriverExecutionProfile)} has been called with a non-null argument. + *

For all the driver's built-in implementations, calling this method with a non-null argument + * automatically resets {@link #getExecutionProfile()} to null. * *

All the driver's built-in implementations are immutable, and return a new instance from this * method. However custom implementations may choose to be mutable and return the same instance. @@ -83,6 +100,9 @@ public interface Statement> extends Request { /** * Sets the execution profile to use for this statement. * + *

For all the driver's built-in implementations, calling this method with a non-null argument + * automatically resets {@link #getExecutionProfileName()} to null. + * *

All the driver's built-in implementations are immutable, and return a new instance from this * method. However custom implementations may choose to be mutable and return the same instance. */ @@ -215,30 +235,88 @@ default SelfT setRoutingKey(@NonNull ByteBuffer... newRoutingKeyComponents) { SelfT setTracing(boolean newTracing); /** - * Returns the query timestamp, in microseconds, to send with the statement. + * @deprecated this method only exists to ease the transition from driver 3, it is an alias for + * {@link #setTracing(boolean) setTracing(true)}. + */ + @Deprecated + @NonNull + @CheckReturnValue + default SelfT enableTracing() { + return setTracing(true); + } + + /** + * @deprecated this method only exists to ease the transition from driver 3, it is an alias for + * {@link #setTracing(boolean) setTracing(false)}. + */ + @Deprecated + @NonNull + @CheckReturnValue + default SelfT disableTracing() { + return setTracing(false); + } + + /** + * Returns the query timestamp, in microseconds, to send with the statement. See {@link + * #setQueryTimestamp(long)} for details. * - *

If this is equal to {@link Long#MIN_VALUE}, the {@link TimestampGenerator} configured for - * this driver instance will be used to generate a timestamp. + *

If this is equal to {@link #NO_DEFAULT_TIMESTAMP}, the {@link TimestampGenerator} configured + * for this driver instance will be used to generate a timestamp. * + * @see #NO_DEFAULT_TIMESTAMP * @see TimestampGenerator */ long getQueryTimestamp(); + /** + * @deprecated this method only exists to ease the transition from driver 3, it is an alias for + * {@link #getQueryTimestamp()}. + */ + @Deprecated + default long getDefaultTimestamp() { + return getQueryTimestamp(); + } + /** * Sets the query timestamp, in microseconds, to send with the statement. * - *

If this is equal to {@link Long#MIN_VALUE}, the {@link TimestampGenerator} configured for - * this driver instance will be used to generate a timestamp. + *

This is an alternative to appending a {@code USING TIMESTAMP} clause in the statement's + * query string, and has the advantage of sending the timestamp separately from the query string + * itself, which doesn't have to be modified when executing the same statement with different + * timestamps. Note that, if both a {@code USING TIMESTAMP} clause and a query timestamp are set + * for a given statement, the timestamp from the {@code USING TIMESTAMP} clause wins. + * + *

This method can be used on any instance of {@link SimpleStatement}, {@link BoundStatement} + * or {@link BatchStatement}. For a {@link BatchStatement}, the timestamp will apply to all its + * child statements; it is not possible to define per-child timestamps using this method, and + * consequently, if this method is called on a batch child statement, the provided timestamp will + * be silently ignored. If different timestamps are required for individual child statements, this + * can only be achieved with a custom {@code USING TIMESTAMP} clause in each child query. + * + *

If this is equal to {@link #NO_DEFAULT_TIMESTAMP}, the {@link TimestampGenerator} configured + * for this driver instance will be used to generate a timestamp. * *

All the driver's built-in implementations are immutable, and return a new instance from this * method. However custom implementations may choose to be mutable and return the same instance. * + * @see #NO_DEFAULT_TIMESTAMP * @see TimestampGenerator */ @NonNull @CheckReturnValue SelfT setQueryTimestamp(long newTimestamp); + /** + * @deprecated this method only exists to ease the transition from driver 3, it is an alias for + * {@link #setQueryTimestamp(long)}. + */ + @Deprecated + @NonNull + @CheckReturnValue + default SelfT setDefaultTimestamp(long newTimestamp) { + return setQueryTimestamp(newTimestamp); + } + /** * Sets how long to wait for this request to complete. This is a global limit on the duration of a * session.execute() call, including any retries the driver might do. @@ -282,6 +360,50 @@ default SelfT setRoutingKey(@NonNull ByteBuffer... newRoutingKeyComponents) { @CheckReturnValue SelfT setPagingState(@Nullable ByteBuffer newPagingState); + /** + * Sets the paging state to send with the statement, or {@code null} if this statement has no + * paging state. + * + *

This variant uses the "safe" paging state wrapper, it will throw immediately if the + * statement doesn't match the one that the state was initially extracted from (same query string, + * same parameters). The advantage is that it fails fast, instead of waiting for an error response + * from the server. + * + *

Note that, if this statement is a {@link SimpleStatement} with bound values, those values + * must be encoded in order to perform the check. This method uses the default codec registry and + * default protocol version. This might fail if you use custom codecs; in that case, use {@link + * #setPagingState(PagingState, Session)} instead. + * + * @throws IllegalArgumentException if the given state does not match this statement. + * @see #setPagingState(ByteBuffer) + * @see ExecutionInfo#getSafePagingState() + */ + @NonNull + @CheckReturnValue + default SelfT setPagingState(@Nullable PagingState newPagingState) { + return setPagingState(newPagingState, null); + } + + /** + * Alternative to {@link #setPagingState(PagingState)} that specifies the session the statement + * will be executed with. You only need this for simple statements, and if you use custom + * codecs. Bound statements already know which session they are attached to. + */ + @NonNull + @CheckReturnValue + default SelfT setPagingState(@Nullable PagingState newPagingState, @Nullable Session session) { + if (newPagingState == null) { + return setPagingState((ByteBuffer) null); + } else if (newPagingState.matches(this, session)) { + return setPagingState(newPagingState.getRawPagingState()); + } else { + throw new IllegalArgumentException( + "Paging state mismatch, " + + "this means that either the paging state contents were altered, " + + "or you're trying to apply it to a different statement"); + } + } + /** * Returns the page size to use for the statement. * @@ -291,6 +413,15 @@ default SelfT setRoutingKey(@NonNull ByteBuffer... newRoutingKeyComponents) { */ int getPageSize(); + /** + * @deprecated this method only exists to ease the transition from driver 3, it is an alias for + * {@link #getPageSize()}. + */ + @Deprecated + default int getFetchSize() { + return getPageSize(); + } + /** * Configures how many rows will be retrieved simultaneously in a single network roundtrip (the * goal being to avoid loading too many results in memory at the same time). @@ -303,6 +434,17 @@ default SelfT setRoutingKey(@NonNull ByteBuffer... newRoutingKeyComponents) { @CheckReturnValue SelfT setPageSize(int newPageSize); + /** + * @deprecated this method only exists to ease the transition from driver 3, it is an alias for + * {@link #setPageSize(int)}. + */ + @Deprecated + @NonNull + @CheckReturnValue + default SelfT setFetchSize(int newPageSize) { + return setPageSize(newPageSize); + } + /** * Returns the {@link ConsistencyLevel} to use for the statement. * @@ -348,6 +490,35 @@ default SelfT setRoutingKey(@NonNull ByteBuffer... newRoutingKeyComponents) { /** Whether tracing information should be recorded for this statement. */ boolean isTracing(); + /** + * A custom "now in seconds" to use when applying the request (for testing purposes). + * + *

This method's default implementation returns {@link #NO_NOW_IN_SECONDS}. The only reason it + * exists is to preserve binary compatibility. Internally, the driver overrides it to return the + * value that was set programmatically (if any). + * + * @see #NO_NOW_IN_SECONDS + */ + default int getNowInSeconds() { + return NO_NOW_IN_SECONDS; + } + + /** + * Sets the "now in seconds" to use when applying the request (for testing purposes). + * + *

This method's default implementation returns the statement unchanged. The only reason it + * exists is to preserve binary compatibility. Internally, the driver overrides it to record the + * new value. + * + * @see #NO_NOW_IN_SECONDS + */ + @NonNull + @CheckReturnValue + @SuppressWarnings("unchecked") + default SelfT setNowInSeconds(int nowInSeconds) { + return (SelfT) this; + } + /** * Calculates the approximate size in bytes that the statement will have when encoded. * diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/cql/StatementBuilder.java b/core/src/main/java/com/datastax/oss/driver/api/core/cql/StatementBuilder.java index 209672fa412..531070b854c 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/cql/StatementBuilder.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/cql/StatementBuilder.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -20,6 +22,7 @@ import com.datastax.oss.driver.api.core.config.DriverExecutionProfile; import com.datastax.oss.driver.api.core.metadata.Node; import com.datastax.oss.driver.api.core.metadata.token.Token; +import com.datastax.oss.driver.internal.core.util.RoutingKey; import com.datastax.oss.protocol.internal.util.collection.NullAllowingImmutableMap; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; @@ -50,13 +53,14 @@ public abstract class StatementBuilder< @Nullable private NullAllowingImmutableMap.Builder customPayloadBuilder; @Nullable protected Boolean idempotent; protected boolean tracing; - protected long timestamp = Long.MIN_VALUE; + protected long timestamp = Statement.NO_DEFAULT_TIMESTAMP; @Nullable protected ByteBuffer pagingState; protected int pageSize = Integer.MIN_VALUE; @Nullable protected ConsistencyLevel consistencyLevel; @Nullable protected ConsistencyLevel serialConsistencyLevel; @Nullable protected Duration timeout; @Nullable protected Node node; + protected int nowInSeconds = Statement.NO_NOW_IN_SECONDS; protected StatementBuilder() { // nothing to do @@ -82,12 +86,16 @@ protected StatementBuilder(StatementT template) { this.serialConsistencyLevel = template.getSerialConsistencyLevel(); this.timeout = template.getTimeout(); this.node = template.getNode(); + this.nowInSeconds = template.getNowInSeconds(); } /** @see Statement#setExecutionProfileName(String) */ @NonNull public SelfT setExecutionProfileName(@Nullable String executionProfileName) { this.executionProfileName = executionProfileName; + if (executionProfileName != null) { + this.executionProfile = null; + } return self; } @@ -95,7 +103,9 @@ public SelfT setExecutionProfileName(@Nullable String executionProfileName) { @NonNull public SelfT setExecutionProfile(@Nullable DriverExecutionProfile executionProfile) { this.executionProfile = executionProfile; - this.executionProfileName = null; + if (executionProfile != null) { + this.executionProfileName = null; + } return self; } @@ -123,6 +133,12 @@ public SelfT setRoutingKey(@Nullable ByteBuffer routingKey) { return self; } + /** @see Statement#setRoutingKey(ByteBuffer...) */ + @NonNull + public SelfT setRoutingKey(@NonNull ByteBuffer... newRoutingKeyComponents) { + return setRoutingKey(RoutingKey.compose(newRoutingKeyComponents)); + } + /** @see Statement#setRoutingToken(Token) */ @NonNull public SelfT setRoutingToken(@Nullable Token routingToken) { @@ -154,13 +170,44 @@ public SelfT setIdempotence(@Nullable Boolean idempotent) { return self; } - /** @see Statement#setTracing(boolean) */ + /** + * This method is a shortcut to {@link #setTracing(boolean)} with an argument of true. It is + * preserved to maintain API compatibility. + * + * @see Statement#setTracing(boolean) + */ @NonNull public SelfT setTracing() { - this.tracing = true; + return setTracing(true); + } + + /** @see Statement#setTracing(boolean) */ + @NonNull + public SelfT setTracing(boolean tracing) { + this.tracing = tracing; return self; } + /** + * @deprecated this method only exists to ease the transition from driver 3, it is an alias for + * {@link #setTracing(boolean) setTracing(true)}. + */ + @Deprecated + @NonNull + public SelfT enableTracing() { + return setTracing(true); + } + + /** + * @deprecated this method only exists to ease the transition from driver 3, it is an alias for + * {@link #setTracing(boolean) setTracing(false)}. + */ + @Deprecated + @NonNull + public SelfT disableTracing() { + return setTracing(false); + } + /** @see Statement#setQueryTimestamp(long) */ @NonNull public SelfT setQueryTimestamp(long timestamp) { @@ -168,6 +215,16 @@ public SelfT setQueryTimestamp(long timestamp) { return self; } + /** + * @deprecated this method only exists to ease the transition from driver 3, it is an alias for + * {@link #setQueryTimestamp(long)}. + */ + @Deprecated + @NonNull + public SelfT setDefaultTimestamp(long timestamp) { + return setQueryTimestamp(timestamp); + } + /** @see Statement#setPagingState(ByteBuffer) */ @NonNull public SelfT setPagingState(@Nullable ByteBuffer pagingState) { @@ -182,6 +239,16 @@ public SelfT setPageSize(int pageSize) { return self; } + /** + * @deprecated this method only exists to ease the transition from driver 3, it is an alias for + * {@link #setPageSize(int)}. + */ + @Deprecated + @NonNull + public SelfT setFetchSize(int pageSize) { + return this.setPageSize(pageSize); + } + /** @see Statement#setConsistencyLevel(ConsistencyLevel) */ @NonNull public SelfT setConsistencyLevel(@Nullable ConsistencyLevel consistencyLevel) { @@ -209,6 +276,12 @@ public SelfT setNode(@Nullable Node node) { return self; } + /** @see Statement#setNowInSeconds(int) */ + public SelfT setNowInSeconds(int nowInSeconds) { + this.nowInSeconds = nowInSeconds; + return self; + } + @NonNull protected Map buildCustomPayload() { return (customPayloadBuilder == null) diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/cql/SyncCqlSession.java b/core/src/main/java/com/datastax/oss/driver/api/core/cql/SyncCqlSession.java new file mode 100644 index 00000000000..a0f752db407 --- /dev/null +++ b/core/src/main/java/com/datastax/oss/driver/api/core/cql/SyncCqlSession.java @@ -0,0 +1,252 @@ +/* + * 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 com.datastax.oss.driver.api.core.cql; + +import com.datastax.oss.driver.api.core.AllNodesFailedException; +import com.datastax.oss.driver.api.core.servererrors.QueryExecutionException; +import com.datastax.oss.driver.api.core.servererrors.QueryValidationException; +import com.datastax.oss.driver.api.core.servererrors.SyntaxError; +import com.datastax.oss.driver.api.core.session.Request; +import com.datastax.oss.driver.api.core.session.Session; +import com.datastax.oss.driver.internal.core.cql.DefaultPrepareRequest; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.Map; +import java.util.Objects; + +/** + * A session that offers user-friendly methods to execute CQL requests synchronously. + * + * @since 4.4.0 + */ +public interface SyncCqlSession extends Session { + + /** + * Executes a CQL statement synchronously (the calling thread blocks until the result becomes + * available). + * + * @param statement the CQL query to execute (that can be any {@link Statement}). + * @return the result of the query. That result will never be null but can be empty (and will be + * for any non SELECT query). + * @throws AllNodesFailedException if no host in the cluster can be contacted successfully to + * execute this query. + * @throws QueryExecutionException if the query triggered an execution exception, i.e. an + * exception thrown by Cassandra when it cannot execute the query with the requested + * consistency level successfully. + * @throws QueryValidationException if the query is invalid (syntax error, unauthorized or any + * other validation problem). + */ + @NonNull + default ResultSet execute(@NonNull Statement statement) { + return Objects.requireNonNull( + execute(statement, Statement.SYNC), "The CQL processor should never return a null result"); + } + + /** + * Executes a CQL statement synchronously (the calling thread blocks until the result becomes + * available). + * + *

This is an alias for {@link #execute(Statement) + * execute(SimpleStatement.newInstance(query))}. + * + * @param query the CQL query to execute. + * @return the result of the query. That result will never be null but can be empty (and will be + * for any non SELECT query). + * @throws AllNodesFailedException if no host in the cluster can be contacted successfully to + * execute this query. + * @throws QueryExecutionException if the query triggered an execution exception, i.e. an + * exception thrown by Cassandra when it cannot execute the query with the requested + * consistency level successfully. + * @throws QueryValidationException if the query if invalid (syntax error, unauthorized or any + * other validation problem). + * @see SimpleStatement#newInstance(String) + */ + @NonNull + default ResultSet execute(@NonNull String query) { + return execute(SimpleStatement.newInstance(query)); + } + + /** + * Executes a CQL statement synchronously (the calling thread blocks until the result becomes + * available). + * + *

This is an alias for {@link #execute(Statement) execute(SimpleStatement.newInstance(query, + * values))}. + * + * @param query the CQL query to execute. + * @param values the values for placeholders in the query string. Individual values can be {@code + * null}, but the vararg array itself can't. + * @return the result of the query. That result will never be null but can be empty (and will be + * for any non SELECT query). + * @throws AllNodesFailedException if no host in the cluster can be contacted successfully to + * execute this query. + * @throws QueryExecutionException if the query triggered an execution exception, i.e. an + * exception thrown by Cassandra when it cannot execute the query with the requested + * consistency level successfully. + * @throws QueryValidationException if the query if invalid (syntax error, unauthorized or any + * other validation problem). + * @see SimpleStatement#newInstance(String, Object...) + */ + @NonNull + default ResultSet execute(@NonNull String query, @NonNull Object... values) { + return execute(SimpleStatement.newInstance(query, values)); + } + + /** + * Executes a CQL statement synchronously (the calling thread blocks until the result becomes + * available). + * + *

This is an alias for {@link #execute(Statement) execute(SimpleStatement.newInstance(query, + * values))}. + * + * @param query the CQL query to execute. + * @param values the values for named placeholders in the query string. Individual values can be + * {@code null}, but the map itself can't. + * @return the result of the query. That result will never be null but can be empty (and will be + * for any non SELECT query). + * @throws AllNodesFailedException if no host in the cluster can be contacted successfully to + * execute this query. + * @throws QueryExecutionException if the query triggered an execution exception, i.e. an + * exception thrown by Cassandra when it cannot execute the query with the requested + * consistency level successfully. + * @throws QueryValidationException if the query if invalid (syntax error, unauthorized or any + * other validation problem). + * @see SimpleStatement#newInstance(String, Map) + */ + @NonNull + default ResultSet execute(@NonNull String query, @NonNull Map values) { + return execute(SimpleStatement.newInstance(query, values)); + } + + /** + * Prepares a CQL statement synchronously (the calling thread blocks until the statement is + * prepared). + * + *

Note that the bound statements created from the resulting prepared statement will inherit + * some of the attributes of the provided simple statement. That is, given: + * + *

{@code
+   * SimpleStatement simpleStatement = SimpleStatement.newInstance("...");
+   * PreparedStatement preparedStatement = session.prepare(simpleStatement);
+   * BoundStatement boundStatement = preparedStatement.bind();
+   * }
+ * + * Then: + * + *
    + *
  • the following methods return the same value as their counterpart on {@code + * simpleStatement}: + *
      + *
    • {@link Request#getExecutionProfileName() boundStatement.getExecutionProfileName()} + *
    • {@link Request#getExecutionProfile() boundStatement.getExecutionProfile()} + *
    • {@link Statement#getPagingState() boundStatement.getPagingState()} + *
    • {@link Request#getRoutingKey() boundStatement.getRoutingKey()} + *
    • {@link Request#getRoutingToken() boundStatement.getRoutingToken()} + *
    • {@link Request#getCustomPayload() boundStatement.getCustomPayload()} + *
    • {@link Request#isIdempotent() boundStatement.isIdempotent()} + *
    • {@link Request#getTimeout() boundStatement.getTimeout()} + *
    • {@link Statement#getPagingState() boundStatement.getPagingState()} + *
    • {@link Statement#getPageSize() boundStatement.getPageSize()} + *
    • {@link Statement#getConsistencyLevel() boundStatement.getConsistencyLevel()} + *
    • {@link Statement#getSerialConsistencyLevel() + * boundStatement.getSerialConsistencyLevel()} + *
    • {@link Statement#isTracing() boundStatement.isTracing()} + *
    + *
  • {@link Request#getRoutingKeyspace() boundStatement.getRoutingKeyspace()} is set from + * either {@link Request#getKeyspace() simpleStatement.getKeyspace()} (if it's not {@code + * null}), or {@code simpleStatement.getRoutingKeyspace()}; + *
  • on the other hand, the following attributes are not propagated: + *
      + *
    • {@link Statement#getQueryTimestamp() boundStatement.getQueryTimestamp()} will be + * set to {@link Statement#NO_DEFAULT_TIMESTAMP}, meaning that the value will be + * assigned by the session's timestamp generator. + *
    • {@link Statement#getNode() boundStatement.getNode()} will always be {@code null}. + *
    • {@link Statement#getNowInSeconds()} boundStatement.getNowInSeconds()} will always + * be equal to {@link Statement#NO_NOW_IN_SECONDS}. + *
    + *
+ * + * If you want to customize this behavior, you can write your own implementation of {@link + * PrepareRequest} and pass it to {@link #prepare(PrepareRequest)}. + * + *

The result of this method is cached: if you call it twice with the same {@link + * SimpleStatement}, you will get the same {@link PreparedStatement} instance. We still recommend + * keeping a reference to it (for example by caching it as a field in a DAO); if that's not + * possible (e.g. if query strings are generated dynamically), it's OK to call this method every + * time: there will just be a small performance overhead to check the internal cache. Note that + * caching is based on: + * + *

    + *
  • the query string exactly as you provided it: the driver does not perform any kind of + * trimming or sanitizing. + *
  • all other execution parameters: for example, preparing two statements with identical + * query strings but different {@linkplain SimpleStatement#getConsistencyLevel() consistency + * levels} will yield distinct prepared statements. + *
+ * + * @param statement the CQL query to execute (that can be any {@link SimpleStatement}). + * @return the prepared statement corresponding to {@code statement}. + * @throws SyntaxError if the syntax of the query to prepare is not correct. + */ + @NonNull + default PreparedStatement prepare(@NonNull SimpleStatement statement) { + return Objects.requireNonNull( + execute(new DefaultPrepareRequest(statement), PrepareRequest.SYNC), + "The CQL prepare processor should never return a null result"); + } + + /** + * Prepares a CQL statement synchronously (the calling thread blocks until the statement is + * prepared). + * + *

The result of this method is cached (see {@link #prepare(SimpleStatement)} for more + * explanations). + * + * @param query the CQL string query to execute. + * @return the prepared statement corresponding to {@code query}. + * @throws SyntaxError if the syntax of the query to prepare is not correct. + */ + @NonNull + default PreparedStatement prepare(@NonNull String query) { + return Objects.requireNonNull( + execute(new DefaultPrepareRequest(query), PrepareRequest.SYNC), + "The CQL prepare processor should never return a null result"); + } + + /** + * Prepares a CQL statement synchronously (the calling thread blocks until the statement is + * prepared). + * + *

This variant is exposed in case you use an ad hoc {@link PrepareRequest} implementation to + * customize how attributes are propagated when you prepare a {@link SimpleStatement} (see {@link + * #prepare(SimpleStatement)} for more explanations). Otherwise, you should rarely have to deal + * with {@link PrepareRequest} directly. + * + *

The result of this method is cached (see {@link #prepare(SimpleStatement)} for more + * explanations). + * + * @param request the {@code PrepareRequest} to execute. + * @return the prepared statement corresponding to {@code request}. + * @throws SyntaxError if the syntax of the query to prepare is not correct. + */ + @NonNull + default PreparedStatement prepare(@NonNull PrepareRequest request) { + return Objects.requireNonNull( + execute(request, PrepareRequest.SYNC), + "The CQL prepare processor should never return a null result"); + } +} diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/cql/TraceEvent.java b/core/src/main/java/com/datastax/oss/driver/api/core/cql/TraceEvent.java index c55e874cdc1..3043d94057f 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/cql/TraceEvent.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/cql/TraceEvent.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -17,6 +19,7 @@ import edu.umd.cs.findbugs.annotations.Nullable; import java.net.InetAddress; +import java.net.InetSocketAddress; /** An event in a {@link QueryTrace}. */ public interface TraceEvent { @@ -28,10 +31,28 @@ public interface TraceEvent { /** The server-side timestamp of the event. */ long getTimestamp(); - /** The IP of the host having generated this event. */ + /** + * @deprecated returns the source IP, but {@link #getSourceAddress()} should be preferred, since + * C* 4.0 and above now returns the port was well. + */ @Nullable + @Deprecated InetAddress getSource(); + /** + * The IP and Port of the host having generated this event. Prior to C* 4.0 the port will be set + * to zero. + * + *

This method's default implementation returns {@link #getSource()} with the port set to 0. + * The only reason it exists is to preserve binary compatibility. Internally, the driver overrides + * it to set the correct port. + * + * @since 4.6.0 + */ + @Nullable + default InetSocketAddress getSourceAddress() { + return new InetSocketAddress(getSource(), 0); + } /** * The number of microseconds elapsed on the source when this event occurred since the moment when * the source started handling the query. diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/data/AccessibleById.java b/core/src/main/java/com/datastax/oss/driver/api/core/data/AccessibleById.java index f4cedb77c31..2ca2222424c 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/data/AccessibleById.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/data/AccessibleById.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -17,7 +19,10 @@ import com.datastax.oss.driver.api.core.CqlIdentifier; import com.datastax.oss.driver.api.core.type.DataType; +import com.datastax.oss.driver.internal.core.util.Loggers; import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.Collections; +import java.util.List; /** * A data structure where the values are accessible via a CQL identifier. @@ -26,6 +31,25 @@ */ public interface AccessibleById extends AccessibleByIndex { + /** + * Returns all the indices where a given identifier appears. + * + * @throws IllegalArgumentException if the id is invalid. + * @apiNote the default implementation only exists for backward compatibility. It wraps the result + * of {@link #firstIndexOf(CqlIdentifier)} in a singleton list, which is not entirely correct, + * as it will only return the first occurrence. Therefore it also logs a warning. + *

Implementors should always override this method (all built-in driver implementations + * do). + */ + @NonNull + default List allIndicesOf(@NonNull CqlIdentifier id) { + Loggers.ACCESSIBLE_BY_ID.warn( + "{} should override allIndicesOf(CqlIdentifier), the default implementation is a " + + "workaround for backward compatibility, it only returns the first occurrence", + getClass().getName()); + return Collections.singletonList(firstIndexOf(id)); + } + /** * Returns the first index where a given identifier appears (depending on the implementation, * identifiers may appear multiple times). diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/data/AccessibleByIndex.java b/core/src/main/java/com/datastax/oss/driver/api/core/data/AccessibleByIndex.java index 509dd4866f9..3007ed1fb68 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/data/AccessibleByIndex.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/data/AccessibleByIndex.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/data/AccessibleByName.java b/core/src/main/java/com/datastax/oss/driver/api/core/data/AccessibleByName.java index ed7359b9c3e..74574a82f38 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/data/AccessibleByName.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/data/AccessibleByName.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -17,7 +19,10 @@ import com.datastax.oss.driver.api.core.CqlIdentifier; import com.datastax.oss.driver.api.core.type.DataType; +import com.datastax.oss.driver.internal.core.util.Loggers; import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.Collections; +import java.util.List; /** * A data structure where the values are accessible via a name string. @@ -42,6 +47,25 @@ */ public interface AccessibleByName extends AccessibleByIndex { + /** + * Returns all the indices where a given identifier appears. + * + * @throws IllegalArgumentException if the name is invalid. + * @apiNote the default implementation only exists for backward compatibility. It wraps the result + * of {@link #firstIndexOf(String)} in a singleton list, which is not entirely correct, as it + * will only return the first occurrence. Therefore it also logs a warning. + *

Implementors should always override this method (all built-in driver implementations + * do). + */ + @NonNull + default List allIndicesOf(@NonNull String name) { + Loggers.ACCESSIBLE_BY_NAME.warn( + "{} should override allIndicesOf(String), the default implementation is a " + + "workaround for backward compatibility, it only returns the first occurrence", + getClass().getName()); + return Collections.singletonList(firstIndexOf(name)); + } + /** * Returns the first index where a given identifier appears (depending on the implementation, * identifiers may appear multiple times). diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/data/ByteUtils.java b/core/src/main/java/com/datastax/oss/driver/api/core/data/ByteUtils.java new file mode 100644 index 00000000000..d3dc68733e4 --- /dev/null +++ b/core/src/main/java/com/datastax/oss/driver/api/core/data/ByteUtils.java @@ -0,0 +1,91 @@ +/* + * 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 com.datastax.oss.driver.api.core.data; + +import com.datastax.oss.protocol.internal.util.Bytes; +import java.nio.ByteBuffer; + +/** + * A set of static utility methods to work with byte buffers (associated with CQL type {@code + * blob}). + */ +public class ByteUtils { + + // Implementation note: this is just a gateway to the internal `Bytes` class in native-protocol. + // The difference is that this one is part of the public API. + + /** + * Converts a blob to its CQL hex string representation. + * + *

A CQL blob string representation consists of the hexadecimal representation of the blob + * bytes prefixed by "0x". + * + * @param bytes the blob/bytes to convert to a string. + * @return the CQL string representation of {@code bytes}. If {@code bytes} is {@code null}, this + * method returns {@code null}. + */ + public static String toHexString(ByteBuffer bytes) { + return Bytes.toHexString(bytes); + } + + /** + * Converts a blob to its CQL hex string representation. + * + *

A CQL blob string representation consists of the hexadecimal representation of the blob + * bytes prefixed by "0x". + * + * @param byteArray the blob/bytes array to convert to a string. + * @return the CQL string representation of {@code bytes}. If {@code bytes} is {@code null}, this + * method returns {@code null}. + */ + public static String toHexString(byte[] byteArray) { + return Bytes.toHexString(byteArray); + } + + /** + * Parses a hex string representing a CQL blob. + * + *

The input should be a valid representation of a CQL blob, i.e. it must start by "0x" + * followed by the hexadecimal representation of the blob bytes. + * + * @param str the CQL blob string representation to parse. + * @return the bytes corresponding to {@code str}. If {@code str} is {@code null}, this method + * returns {@code null}. + * @throws IllegalArgumentException if {@code str} is not a valid CQL blob string. + */ + public static ByteBuffer fromHexString(String str) { + return Bytes.fromHexString(str); + } + + /** + * Extracts the content of the provided {@code ByteBuffer} as a byte array. + * + *

This method works with any type of {@code ByteBuffer} (direct and non-direct ones), but when + * the buffer is backed by an array, it will try to avoid copy when possible. As a consequence, + * changes to the returned byte array may or may not reflect into the initial buffer. + * + * @param bytes the buffer whose contents to extract. + * @return a byte array with the contents of {@code bytes}. That array may be the array backing + * {@code bytes} if this can avoid a copy. + */ + public static byte[] getArray(ByteBuffer bytes) { + return Bytes.getArray(bytes); + } + + private ByteUtils() {} +} diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/data/CqlDuration.java b/core/src/main/java/com/datastax/oss/driver/api/core/data/CqlDuration.java index 1db7e1d8d4f..bfa9df20bbb 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/data/CqlDuration.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/data/CqlDuration.java @@ -1,11 +1,13 @@ /* - * Copyright DataStax, Inc. + * 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 * - * 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 + * 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, @@ -20,6 +22,7 @@ import com.datastax.oss.driver.shaded.guava.common.base.Preconditions; import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableList; import edu.umd.cs.findbugs.annotations.NonNull; +import java.io.Serializable; import java.time.Duration; import java.time.Period; import java.time.temporal.ChronoUnit; @@ -28,6 +31,7 @@ import java.time.temporal.TemporalUnit; import java.time.temporal.UnsupportedTemporalTypeException; import java.util.List; +import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; import net.jcip.annotations.Immutable; @@ -41,7 +45,9 @@ * in time, regardless of the calendar). */ @Immutable -public final class CqlDuration implements TemporalAmount { +public final class CqlDuration implements TemporalAmount, Serializable { + + private static final long serialVersionUID = 1L; @VisibleForTesting static final long NANOS_PER_MICRO = 1000L; @VisibleForTesting static final long NANOS_PER_MILLI = 1000 * NANOS_PER_MICRO; @@ -74,14 +80,17 @@ public final class CqlDuration implements TemporalAmount { private static final ImmutableList TEMPORAL_UNITS = ImmutableList.of(ChronoUnit.MONTHS, ChronoUnit.DAYS, ChronoUnit.NANOS); + /** @serial */ private final int months; + /** @serial */ private final int days; + /** @serial */ private final long nanoseconds; private CqlDuration(int months, int days, long nanoseconds) { // Makes sure that all the values are negative if one of them is if ((months < 0 || days < 0 || nanoseconds < 0) - && ((months > 0 || days > 0 || nanoseconds > 0))) { + && (months > 0 || days > 0 || nanoseconds > 0)) { throw new IllegalArgumentException( String.format( "All values must be either negative or positive, got %d months, %d days, %d nanoseconds", @@ -115,7 +124,7 @@ public static CqlDuration newInstance(int months, int days, long nanoseconds) { *

  • multiple digits followed by a time unit like: 12h30m where the time unit can be: *