├── .gitignore ├── duke_logo.png ├── .github ├── dependabot.yml ├── workflows │ ├── semantic.yml │ ├── codeql-analysis.yml │ └── linter.yml ├── PULL_REQUEST_TEMPLATE.md └── ISSUE_TEMPLATE │ ├── SUPPORT.yml │ ├── FEATURE_REQUEST.yml │ └── BUG_REPORT.yml ├── .markdownlint.yml ├── examples ├── .gitignore ├── docker-compose.yml ├── README.md ├── src │ └── main │ │ └── java │ │ └── com │ │ └── influxdb │ │ └── v3 │ │ ├── ProxyExample.java │ │ ├── TimeoutsExample.java │ │ ├── DownsamplingExample.java │ │ ├── IOxExample.java │ │ ├── durable │ │ ├── InfluxClientPool.java │ │ └── Sensor.java │ │ └── RetryExample.java ├── envoy.yaml └── pom.xml ├── license_header.txt ├── LICENSE ├── src ├── main │ └── java │ │ └── com │ │ └── influxdb │ │ └── v3 │ │ └── client │ │ ├── query │ │ ├── QueryType.java │ │ └── QueryOptions.java │ │ ├── write │ │ ├── WritePrecision.java │ │ └── WritePrecisionConverter.java │ │ ├── InfluxDBApiException.java │ │ ├── internal │ │ ├── Identity.java │ │ ├── TypeCasting.java │ │ ├── Arguments.java │ │ ├── NanosecondConverter.java │ │ └── VectorSchemaRootConverter.java │ │ └── InfluxDBApiHttpException.java ├── test │ └── java │ │ └── com │ │ └── influxdb │ │ └── v3 │ │ └── client │ │ ├── testdata │ │ ├── influxdb-certificate.pem │ │ └── docker.com.pem │ │ ├── internal │ │ ├── TypeCastingTest.java │ │ ├── ArgumentsDurationTest.java │ │ ├── NanosecondConverterTest.java │ │ └── ArgumentsTest.java │ │ ├── write │ │ └── WritePrecisionConverterTest.java │ │ ├── AbstractMockServerTest.java │ │ ├── TestUtils.java │ │ ├── InfluxDBClientTest.java │ │ ├── issues │ │ └── MemoryLeakIssueTest.java │ │ ├── PointValuesTest.java │ │ └── PointTest.java └── site │ └── site.xml ├── deploy-settings.xml ├── maven-version-rules.xml ├── scripts └── influxdb-setup.sh ├── checkstyle.xml ├── README.md ├── .circleci └── config.yml └── CHANGELOG.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | target/ 3 | .java-version 4 | -------------------------------------------------------------------------------- /duke_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InfluxCommunity/influxdb3-java/HEAD/duke_logo.png -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "maven" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | -------------------------------------------------------------------------------- /.markdownlint.yml: -------------------------------------------------------------------------------- 1 | { 2 | "MD013": false, 3 | "MD024": { 4 | "siblings_only": true 5 | }, 6 | "MD033": { 7 | "allowed_elements": [ "a", "img", "p" ] 8 | }, 9 | "MD041": false, 10 | } -------------------------------------------------------------------------------- /.github/workflows/semantic.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Semantic PR and Commit Messages" 3 | 4 | on: 5 | pull_request: 6 | types: [opened, reopened, synchronize, edited] 7 | branches: 8 | - main 9 | 10 | jobs: 11 | semantic: 12 | uses: influxdata/validate-semantic-github-messages/.github/workflows/semantic.yml@main 13 | with: 14 | CHECK_PR_TITLE_OR_ONE_COMMIT: true 15 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Closes # 2 | 3 | ## Proposed Changes 4 | 5 | _Briefly describe your proposed changes:_ 6 | 7 | ## Checklist 8 | 9 | 10 | 11 | - [ ] CHANGELOG.md updated 12 | - [ ] Rebased/mergeable 13 | - [ ] A test has been added if appropriate 14 | - [ ] Tests pass 15 | - [ ] Commit messages are [conventional](https://www.conventionalcommits.org/en/v1.0.0/) 16 | - [ ] Sign [CLA](https://www.influxdata.com/legal/cla/) (if not already signed) 17 | -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | !**/src/main/**/target/ 4 | !**/src/test/**/target/ 5 | 6 | ### IntelliJ IDEA ### 7 | .idea/modules.xml 8 | .idea/jarRepositories.xml 9 | .idea/compiler.xml 10 | .idea/libraries/ 11 | *.iws 12 | *.iml 13 | *.ipr 14 | 15 | ### Eclipse ### 16 | .apt_generated 17 | .classpath 18 | .factorypath 19 | .project 20 | .settings 21 | .springBeans 22 | .sts4-cache 23 | 24 | ### NetBeans ### 25 | /nbproject/private/ 26 | /nbbuild/ 27 | /dist/ 28 | /nbdist/ 29 | /.nb-gradle/ 30 | build/ 31 | !**/src/main/**/build/ 32 | !**/src/test/**/build/ 33 | 34 | ### VS Code ### 35 | .vscode/ 36 | 37 | ### Mac OS ### 38 | .DS_Store -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/SUPPORT.yml: -------------------------------------------------------------------------------- 1 | name: Support request 2 | description: Open a support request 3 | labels: ["support"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | WOAHH, hold up. This isn't the best place for support questions. 9 | You can get a faster response on slack or forums: 10 | 11 | Please redirect any QUESTIONS about Telegraf usage to 12 | - InfluxData Slack Channel: https://app.slack.com/huddle/TH8RGQX5Z/C02UDUPLQKA 13 | - InfluxData Community Site: https://community.influxdata.com 14 | 15 | - type: textarea 16 | attributes: 17 | label: "Please direct all support questions to slack or the forums. Thank you." 18 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | CodeQL-Build: 11 | runs-on: ubuntu-latest 12 | 13 | permissions: 14 | security-events: write 15 | actions: read 16 | contents: read 17 | 18 | steps: 19 | - name: Checkout repository 20 | uses: actions/checkout@v3 21 | 22 | - name: Initialize CodeQL 23 | uses: github/codeql-action/init@v2 24 | with: 25 | languages: java 26 | 27 | - name: Autobuild 28 | uses: github/codeql-action/autobuild@v2 29 | 30 | - name: Perform CodeQL Analysis 31 | uses: github/codeql-action/analyze@v2 32 | -------------------------------------------------------------------------------- /.github/workflows/linter.yml: -------------------------------------------------------------------------------- 1 | name: "Lint Code Base" 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | jobs: 10 | build: 11 | permissions: 12 | contents: read 13 | packages: read 14 | statuses: write 15 | 16 | name: Lint Code Base 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - name: Checkout Code 21 | uses: actions/checkout@v3 22 | with: 23 | fetch-depth: 0 24 | 25 | - name: Lint Code Base 26 | uses: github/super-linter@v4.9.2 27 | env: 28 | VALIDATE_ALL_CODEBASE: false 29 | DEFAULT_BRANCH: main 30 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 31 | LINTER_RULES_PATH: '.' 32 | MARKDOWN_CONFIG_FILE: .markdownlint.yml 33 | VALIDATE_MARKDOWN: true 34 | VALIDATE_BASH: true 35 | VALIDATE_JAVA: true 36 | JAVA_FILE_NAME: 'checkstyle.xml' 37 | VALIDATE_GITHUB_ACTIONS: true 38 | -------------------------------------------------------------------------------- /license_header.txt: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 InfluxData Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/FEATURE_REQUEST.yml: -------------------------------------------------------------------------------- 1 | name: Feature request 2 | description: Create a feature request to make client more awesome 3 | labels: ["feature request"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Thanks for taking time to share with us this feature request! Please describe why you would like this feature to be added to the client, how you plan to use it to make your life better. 9 | - type: textarea 10 | id: use-case 11 | attributes: 12 | label: Use Case 13 | description: Describe how you plan to use this feature. 14 | validations: 15 | required: true 16 | - type: textarea 17 | id: expected-behavior 18 | attributes: 19 | label: Expected behavior 20 | description: Describe what you expected to happen when you performed the above steps. 21 | validations: 22 | required: true 23 | - type: textarea 24 | id: actual-behavior 25 | attributes: 26 | label: Actual behavior 27 | description: Describe what actually happened when you performed the above steps. 28 | validations: 29 | required: true 30 | - type: textarea 31 | id: additional-info 32 | attributes: 33 | label: Additional info 34 | description: Include gist of relevant config, logs, etc. 35 | validations: 36 | required: false 37 | 38 | -------------------------------------------------------------------------------- /examples/docker-compose.yml: -------------------------------------------------------------------------------- 1 | # 2 | # The MIT License 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | # 22 | 23 | version: "3" 24 | 25 | services: 26 | envoy: 27 | image: envoyproxy/envoy:v1.26-latest 28 | volumes: 29 | - ./envoy.yaml:/etc/envoy/envoy.yaml 30 | ports: 31 | - "10000:10000" 32 | environment: 33 | - ENVOY_UID=0 34 | 35 | -------------------------------------------------------------------------------- /src/main/java/com/influxdb/v3/client/query/QueryType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client.query; 23 | 24 | /** 25 | * Defines type of query sent to InfluxDB. 26 | */ 27 | public enum QueryType { 28 | 29 | /** 30 | * Query by SQL. 31 | */ 32 | SQL, 33 | 34 | /** 35 | * Query by InfluxQL. 36 | */ 37 | InfluxQL 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/influxdb/v3/client/write/WritePrecision.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client.write; 23 | 24 | /** 25 | * Defines WritePrecision. 26 | */ 27 | public enum WritePrecision { 28 | 29 | /** 30 | * Time precision in milliseconds. 31 | */ 32 | MS, 33 | /** 34 | * Time precision in seconds. 35 | */ 36 | S, 37 | /** 38 | * Time precision in microseconds. 39 | */ 40 | US, 41 | /** 42 | * Time precision in nanoseconds. 43 | */ 44 | NS 45 | } 46 | 47 | -------------------------------------------------------------------------------- /src/main/java/com/influxdb/v3/client/InfluxDBApiException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client; 23 | 24 | import javax.annotation.Nullable; 25 | 26 | /** 27 | * The InfluxDBApiException is thrown when an error occurs while interacting with InfluxDB. 28 | */ 29 | public class InfluxDBApiException extends RuntimeException { 30 | /** 31 | * Construct a new InfluxDBApiException with the specified detail message. 32 | * 33 | * @param message the detail message. 34 | */ 35 | public InfluxDBApiException(@Nullable final String message) { 36 | super(message); 37 | } 38 | 39 | /** 40 | * Construct a new InfluxDBApiException with the specified cause. 41 | * 42 | * @param cause the cause. 43 | */ 44 | public InfluxDBApiException(@Nullable final Throwable cause) { 45 | super(cause); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/influxdb/v3/client/internal/Identity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client.internal; 23 | 24 | /** 25 | * Functions for establishing caller identity. 26 | */ 27 | final class Identity { 28 | private Identity() { } 29 | 30 | /** 31 | * Attempt to get the package version. 32 | * @return - package version or unknown. 33 | */ 34 | static String getVersion() { 35 | Package mainPackage = Identity.class.getPackage(); 36 | String version = mainPackage != null ? mainPackage.getImplementationVersion() : "unknown"; 37 | return version == null ? "unknown" : version; 38 | } 39 | 40 | /** 41 | * Get a standard user-agent identity to be used in all HTTP based calls. 42 | * @return - the standard user-agent string. 43 | */ 44 | static String getUserAgent() { 45 | return String.format("influxdb3-java/%s", getVersion()); 46 | } 47 | } -------------------------------------------------------------------------------- /src/test/java/com/influxdb/v3/client/testdata/influxdb-certificate.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw 3 | TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh 4 | cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 5 | WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu 6 | ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY 7 | MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc 8 | h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ 9 | 0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U 10 | A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW 11 | T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH 12 | B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC 13 | B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv 14 | KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn 15 | OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn 16 | jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw 17 | qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI 18 | rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV 19 | HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq 20 | hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL 21 | ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ 22 | 3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK 23 | NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 24 | ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur 25 | TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC 26 | jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc 27 | oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq 28 | 4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA 29 | mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d 30 | emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= 31 | -----END CERTIFICATE----- 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/BUG_REPORT.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Create a bug report to help us improve 3 | labels: ["bug"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Thanks for taking time to fill out this bug report! We reserve this repository issues for bugs with reproducible problems. 9 | Please redirect any questions about the client usage to our [Community Slack](https://app.slack.com/huddle/TH8RGQX5Z/C02UDUPLQKA) or [Community Page](https://community.influxdata.com/) we have a lot of talented community members there who could help answer your question more quickly. 10 | 11 | * Please add a :+1: or comment on a similar existing bug report instead of opening a new one. 12 | * Please check whether the bug can be reproduced with the latest release. 13 | - type: textarea 14 | id: specifications 15 | attributes: 16 | label: Specifications 17 | description: Describe the steps to reproduce the bug. 18 | value: | 19 | * Client Version: 20 | * InfluxDB Version: 21 | * Platform: 22 | validations: 23 | required: true 24 | - type: textarea 25 | id: reproduce 26 | attributes: 27 | label: Code sample to reproduce problem 28 | description: Provide a code sample that reproduces the problem 29 | value: | 30 | ```java 31 | ``` 32 | validations: 33 | required: true 34 | - type: textarea 35 | id: expected-behavior 36 | attributes: 37 | label: Expected behavior 38 | description: Describe what you expected to happen when you performed the above steps. 39 | validations: 40 | required: true 41 | - type: textarea 42 | id: actual-behavior 43 | attributes: 44 | label: Actual behavior 45 | description: Describe what actually happened when you performed the above steps. 46 | validations: 47 | required: true 48 | - type: textarea 49 | id: additional-info 50 | attributes: 51 | label: Additional info 52 | description: Include gist of relevant config, logs, etc. 53 | validations: 54 | required: false -------------------------------------------------------------------------------- /deploy-settings.xml: -------------------------------------------------------------------------------- 1 | 24 | 26 | 27 | 28 | ossrh 29 | ${env.SONATYPE_USERNAME} 30 | ${env.SONATYPE_PASSWORD} 31 | 32 | 33 | 34 | 35 | 36 | ossrh 37 | 38 | true 39 | 40 | 41 | ${env.GPG_EXECUTABLE} 42 | ${env.GPG_PASSPHRASE} 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/test/java/com/influxdb/v3/client/testdata/docker.com.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFxDCCBKygAwIBAgIQDE0Qon/WHglZ2jXwZzD0xjANBgkqhkiG9w0BAQsFADA8 3 | MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRwwGgYDVQQDExNBbWF6b24g 4 | UlNBIDIwNDggTTAyMB4XDTI0MDgxODAwMDAwMFoXDTI1MDkxNjIzNTk1OVowFzEV 5 | MBMGA1UEAwwMKi5kb2NrZXIuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB 6 | CgKCAQEAytONEwLLBXfyepD/iE7N76+xGDWE/7g21rpPRejCobpaKGVKSL4Y8Pf9 7 | whjh5pzBLxcpQMpYcg/oW+Cp4scjhXyi9yqrOC2Vf26DRA3ufjbescZUjPP28mPO 8 | N1gGQhnr0Sa7mbhNo5JVE7yxLrjhAZFCdEpl1LYdfosYxeBowVOaxBsGfUWCxYuI 9 | HbolTUUNWJKaAN7knKmrHF0a2a6BftaTyFK/6N1FV3rXs5oD+5DEYVFN8193fdz5 10 | DUcf5p7xzjx9yXmHfdomznUPL5Sja2FSigH+Gm6EG3cBKylVCpafwQhbYbpdkP12 11 | p7KtsPGUWIwBAyRr1AUqx0ceIafa9wIDAQABo4IC5TCCAuEwHwYDVR0jBBgwFoAU 12 | wDFSzVpQw4J8dHHOy+mc+XrrguIwHQYDVR0OBBYEFFgCnhEuuG5ptVcvPfBJZssU 13 | U6jFMBcGA1UdEQQQMA6CDCouZG9ja2VyLmNvbTATBgNVHSAEDDAKMAgGBmeBDAEC 14 | ATAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC 15 | MDsGA1UdHwQ0MDIwMKAuoCyGKmh0dHA6Ly9jcmwucjJtMDIuYW1hem9udHJ1c3Qu 16 | Y29tL3IybTAyLmNybDB1BggrBgEFBQcBAQRpMGcwLQYIKwYBBQUHMAGGIWh0dHA6 17 | Ly9vY3NwLnIybTAyLmFtYXpvbnRydXN0LmNvbTA2BggrBgEFBQcwAoYqaHR0cDov 18 | L2NydC5yMm0wMi5hbWF6b250cnVzdC5jb20vcjJtMDIuY2VyMAwGA1UdEwEB/wQC 19 | MAAwggF+BgorBgEEAdZ5AgQCBIIBbgSCAWoBaAB3AN3cyjSV1+EWBeeVMvrHn/g9 20 | HFDf2wA6FBJ2Ciysu8gqAAABkWTCKI4AAAQDAEgwRgIhALzND2kn+PR4aObDiWGv 21 | 7QJZ/z4i/vhAGjd/vp2hjt4HAiEAytgsAcPUFd/Y98n5EQkMg6GBSUUygBMc7+Xk 22 | UVJXFgcAdQDm0jFjQHeMwRBBBtdxuc7B0kD2loSG+7qHMh39HjeOUAAAAZFkwiic 23 | AAAEAwBGMEQCIGXbP5ughe+EksczYEErOqY1LZHi9/SmXcp6/vAeuXBmAiB16Sw7 24 | 1sPB/CpKq75/pEwb5pamTdmeEIaHtGQTkTMZ5QB2AMz7D2qFcQll/pWbU87psnwi 25 | 6YVcDZeNtql+VMD+TA2wAAABkWTCKIoAAAQDAEcwRQIgX2WsD3ThDwalMCYqmf+X 26 | ICS9imyYmwKXJbfkSnMH/HkCIQCEkDrOAJZG1fGfyOzhSPHWPMxbuGwV18jBGe52 27 | D9PPQTANBgkqhkiG9w0BAQsFAAOCAQEAcW8Z3+JzqfGTYX+MBk2pQvo8msx+fINU 28 | ZzORUcFUNV0467G/Kc780S8GRxF8dr90WaFctMvw8yDOipfj0sacGJFVFFf5XoWu 29 | 1EqYyx3hgMLCQ5DzjAoY4X1KsfPRBe2DCsqf+Nt/TVzgVjOSglURBWKV2T+Av78H 30 | HiMjtEUZbatvuKvUg5S26dvUxXseN/8Jbbt9MCCGascPOf+zXGysoBSwglt0Nz7W 31 | hjXnY+BPeLD38ouUGx0R+sZbXICR6BIxtT4GgOcmJJ/KT3fcMObhsihiq6lO1aql 32 | Yo4AvWpg2tJtwFrG4ooDRyyARljHMUMIkuTz9Dypl/dnJJfzAQLjVA== 33 | -----END CERTIFICATE----- 34 | -------------------------------------------------------------------------------- /maven-version-rules.xml: -------------------------------------------------------------------------------- 1 | 2 | 25 | 26 | 27 | (?i).*Alpha(?:-?\d+)? 28 | (?i).*a(?:-?\d+)? 29 | (?i).*Beta(?:-?\d+)? 30 | (?i).*-B(?:-?\d+)? 31 | (?i).*RC(?:-?\d+)? 32 | (?i).*CR(?:-?\d+)? 33 | (?i).*M(?:-?\d+)? 34 | (?i).*SNAP(?:-?\d+)? 35 | .*[-_\.](alpha|Alpha|ALPHA|beta|Beta|BETA|rc|‌​RC)[-_\.]?.* 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/main/java/com/influxdb/v3/client/write/WritePrecisionConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client.write; 23 | 24 | public final class WritePrecisionConverter { 25 | 26 | private WritePrecisionConverter() { 27 | } 28 | 29 | public static String toV2ApiString(final WritePrecision precision) { 30 | switch (precision) { 31 | case NS: 32 | return "ns"; 33 | case US: 34 | return "us"; 35 | case MS: 36 | return "ms"; 37 | case S: 38 | return "s"; 39 | default: 40 | throw new IllegalArgumentException("Unsupported precision '" + precision + "'"); 41 | } 42 | } 43 | 44 | public static String toV3ApiString(final WritePrecision precision) { 45 | switch (precision) { 46 | case NS: 47 | return "nanosecond"; 48 | case US: 49 | return "microsecond"; 50 | case MS: 51 | return "millisecond"; 52 | case S: 53 | return "second"; 54 | default: 55 | throw new IllegalArgumentException("Unsupported precision '" + precision + "'"); 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /src/site/site.xml: -------------------------------------------------------------------------------- 1 | 24 | 26 | 27 | ${project.name} 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | org.apache.maven.skins 50 | maven-fluido-skin 51 | 1.7 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /src/test/java/com/influxdb/v3/client/internal/TypeCastingTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client.internal; 23 | 24 | import org.apache.arrow.vector.util.Text; 25 | import org.junit.jupiter.api.Assertions; 26 | import org.junit.jupiter.api.Test; 27 | 28 | public class TypeCastingTest { 29 | 30 | @Test 31 | void testToLongValue() { 32 | Assertions.assertEquals(1, TypeCasting.toLongValue(1)); 33 | Assertions.assertEquals(1.0, TypeCasting.toLongValue(1.23)); 34 | 35 | Assertions.assertThrows(ClassCastException.class, 36 | () -> TypeCasting.toLongValue("1")); 37 | } 38 | 39 | @Test 40 | void testToDoubleValue() { 41 | Assertions.assertEquals(1.23, TypeCasting.toDoubleValue(1.23)); 42 | Assertions.assertEquals(1.0, TypeCasting.toDoubleValue(1)); 43 | 44 | Assertions.assertThrows(ClassCastException.class, 45 | () -> TypeCasting.toDoubleValue("1.2")); 46 | } 47 | 48 | @Test 49 | void testToStringValue() { 50 | Assertions.assertEquals("test", TypeCasting.toStringValue("test")); 51 | Assertions.assertEquals("test", 52 | TypeCasting.toStringValue(new Text("test"))); 53 | 54 | Assertions.assertThrows(ClassCastException.class, 55 | () -> TypeCasting.toStringValue(1)); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | > :warning: The examples depends on the "influxdb3-java" module and this module should be built first by running "mvn install" in the root directory. 4 | > :warning: Some JDK internals must be exposed by adding `--add-opens=java.base/java.nio=ALL-UNNAMED` to your JVM arguments. 5 | 6 | ## Basic 7 | 8 | - [IOxExample](src/main/java/com/influxdb/v3/IOxExample.java) - How to use write and query data from InfluxDB IOx 9 | 10 | ## RetryExample 11 | 12 | - [RetryExample](src/main/java/com/influxdb/v3/RetryExample.java) - How to catch an `InfluxDBApiHttpException` to then retry making write requests. 13 | 14 | ### Command line run 15 | 16 | - Set environment variables. 17 | 18 | ```bash 19 | 20 | export INFLUX_HOST= 21 | export INFLUX_TOKEN= 22 | export INFLUX_DATABASE= 23 | 24 | ``` 25 | 26 | - Run with maven 27 | 28 | ```bash 29 | mvn compile exec:java -Dexec.main="com.influxdb.v3.RetryExample" 30 | ``` 31 | 32 | - Repeat previous step to force an HTTP 429 response and rewrite attempt. 33 | 34 | ## Durable example 35 | 36 | This example illustrates one approach to ensuring clients, once initialized, are long-lived and reused. 37 | 38 | The underlying write (HTTP/REST) and query (Apache arrow Flight/GRPC) transports are designed to be robust and to be able to recover from most errors. The InfluxDBClient query API is based on GRPC stubs and channels. [GRPC best practices](https://grpc.io/docs/guides/performance/) recommends reusing them and their resources for the life of an application if at all possible. Unnecessary frequent regeneration of InfluxDBClient instances is wasteful of system resources. Recreating the query transport means fully recreating a GRPC channel, its connection pool and its management API. Fully recreating a client only to use it for a single query also means recreating an unused write transport alongside the query transport. This example attempts to show a more resource friendly use of the API by leveraging already used client instances. 39 | 40 | - [DurableExample](src/main/java/com/influxdb/v3/durable/DurableExample.java) 41 | - [InfluxClientPool](src/main/java/com/influxdb/v3/durable/InfluxClientPool.java) 42 | 43 | ### Command line run 44 | 45 | - Set environment variables 46 | 47 | ```bash 48 | 49 | export INFLUX_HOST= 50 | export INFLUX_TOKEN= 51 | export INFLUX_DATABASE= 52 | 53 | ``` 54 | 55 | - Run with maven 56 | 57 | ```bash 58 | MAVEN_OPTS="--add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED" mvn compile exec:java -Dexec.main="com.influxdb.v3.durable.DurableExample" 59 | ``` 60 | -------------------------------------------------------------------------------- /src/test/java/com/influxdb/v3/client/write/WritePrecisionConverterTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client.write; 23 | 24 | import java.util.Map; 25 | 26 | import org.junit.jupiter.api.Test; 27 | 28 | import static org.junit.jupiter.api.Assertions.assertEquals; 29 | 30 | public class WritePrecisionConverterTest { 31 | @Test 32 | void toV2ApiString() { 33 | Map testCases = Map.of( 34 | WritePrecision.NS, "ns", 35 | WritePrecision.US, "us", 36 | WritePrecision.MS, "ms", 37 | WritePrecision.S, "s" 38 | ); 39 | 40 | for (Map.Entry e : testCases.entrySet()) { 41 | WritePrecision precision = e.getKey(); 42 | String expectedString = e.getValue(); 43 | String result = WritePrecisionConverter.toV2ApiString(precision); 44 | assertEquals(expectedString, result, "Failed for precision: " + precision); 45 | } 46 | } 47 | 48 | @Test 49 | void toV3ApiString() { 50 | Map tc = Map.of( 51 | WritePrecision.NS, "nanosecond", 52 | WritePrecision.US, "microsecond", 53 | WritePrecision.MS, "millisecond", 54 | WritePrecision.S, "second" 55 | ); 56 | 57 | for (Map.Entry e : tc.entrySet()) { 58 | WritePrecision precision = e.getKey(); 59 | String expectedString = e.getValue(); 60 | String result = WritePrecisionConverter.toV3ApiString(precision); 61 | assertEquals(expectedString, result, "Failed for precision: " + precision); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/influxdb/v3/client/internal/TypeCasting.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client.internal; 23 | 24 | import javax.annotation.Nonnull; 25 | 26 | import org.apache.arrow.vector.util.Text; 27 | 28 | /** 29 | * Functions for safe type casting. 30 | */ 31 | public final class TypeCasting { 32 | 33 | private TypeCasting() { } 34 | 35 | /** 36 | * Safe casting to long value. 37 | * 38 | * @param value object to cast 39 | * @return long value 40 | */ 41 | public static long toLongValue(@Nonnull final Object value) { 42 | 43 | if (long.class.isAssignableFrom(value.getClass()) 44 | || Long.class.isAssignableFrom(value.getClass())) { 45 | return (long) value; 46 | } 47 | 48 | return ((Number) value).longValue(); 49 | } 50 | 51 | /** 52 | * Safe casting to double value. 53 | * 54 | * @param value object to cast 55 | * @return double value 56 | */ 57 | public static double toDoubleValue(@Nonnull final Object value) { 58 | 59 | if (double.class.isAssignableFrom(value.getClass()) 60 | || Double.class.isAssignableFrom(value.getClass())) { 61 | return (double) value; 62 | } 63 | 64 | return ((Number) value).doubleValue(); 65 | } 66 | 67 | /** 68 | * Safe casting to string value. 69 | * 70 | * @param value object to cast 71 | * @return string value 72 | */ 73 | public static String toStringValue(@Nonnull final Object value) { 74 | 75 | if (Text.class.isAssignableFrom(value.getClass())) { 76 | return value.toString(); 77 | } 78 | 79 | return (String) value; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /examples/src/main/java/com/influxdb/v3/ProxyExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3; 23 | 24 | import java.util.UUID; 25 | import java.util.stream.Stream; 26 | 27 | import com.influxdb.v3.client.InfluxDBClient; 28 | import com.influxdb.v3.client.Point; 29 | import com.influxdb.v3.client.PointValues; 30 | import com.influxdb.v3.client.config.ClientConfig; 31 | 32 | public final class ProxyExample { 33 | 34 | private ProxyExample() { 35 | } 36 | 37 | public static void main(final String[] args) throws Exception { 38 | // Run docker-compose.yml file to start Envoy proxy 39 | 40 | String proxyUrl = "http://localhost:10000"; 41 | String sslRootsFilePath = "src/test/java/com/influxdb/v3/client/testdata/influxdb-certificate.pem"; 42 | ClientConfig clientConfig = new ClientConfig.Builder() 43 | .host(System.getenv("INFLUXDB_URL")) 44 | .token(System.getenv("INFLUXDB_TOKEN").toCharArray()) 45 | .database(System.getenv("INFLUXDB_DATABASE")) 46 | .proxyUrl(proxyUrl) 47 | .sslRootsFilePath(sslRootsFilePath) 48 | .build(); 49 | 50 | InfluxDBClient influxDBClient = InfluxDBClient.getInstance(clientConfig); 51 | String testId = UUID.randomUUID().toString(); 52 | Point point = Point.measurement("My_Home") 53 | .setTag("room", "Kitchen") 54 | .setField("temp", 12.7) 55 | .setField("hum", 37) 56 | .setField("testId", testId); 57 | influxDBClient.writePoint(point); 58 | 59 | String query = String.format("SELECT * FROM \"My_Home\" WHERE \"testId\" = '%s'", testId); 60 | try (Stream stream = influxDBClient.queryPoints(query)) { 61 | stream.findFirst().ifPresent(values -> { 62 | assert values.getTimestamp() != null; 63 | System.out.printf("room[%s]: %s, temp: %3.2f, hum: %d", 64 | new java.util.Date(values.getTimestamp().longValue() / 1000000), 65 | values.getTag("room"), 66 | (Double) values.getField("temp"), 67 | (Long) values.getField("hum")); 68 | }); 69 | } 70 | } 71 | } 72 | 73 | -------------------------------------------------------------------------------- /src/test/java/com/influxdb/v3/client/internal/ArgumentsDurationTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client.internal; 23 | 24 | import org.assertj.core.api.Assertions; 25 | import org.junit.jupiter.api.Test; 26 | 27 | /** 28 | * @author Jakub Bednar (bednar@github) (20/08/2018 12:31) 29 | */ 30 | @SuppressWarnings("ConstantConditions") 31 | class ArgumentsDurationTest { 32 | 33 | @Test 34 | void literals() { 35 | 36 | Arguments.checkDuration("1s", "duration"); 37 | Arguments.checkDuration("10d", "duration"); 38 | Arguments.checkDuration("1h15m", "duration"); 39 | Arguments.checkDuration("5w", "duration"); 40 | Arguments.checkDuration("1mo5d", "duration"); 41 | Arguments.checkDuration("-1mo5d", "duration"); 42 | Arguments.checkDuration("inf", "duration"); 43 | Arguments.checkDuration("-inf", "duration"); 44 | } 45 | 46 | @Test 47 | void literalNull() { 48 | 49 | Assertions.assertThatThrownBy(() -> Arguments.checkDuration(null, "duration")) 50 | .isInstanceOf(IllegalArgumentException.class) 51 | .hasMessage("Expecting a duration string for duration. But got: null"); 52 | } 53 | 54 | @Test 55 | void literalEmpty() { 56 | 57 | Assertions.assertThatThrownBy(() -> Arguments.checkDuration("", "duration")) 58 | .isInstanceOf(IllegalArgumentException.class) 59 | .hasMessage("Expecting a duration string for duration. But got: "); 60 | } 61 | 62 | @Test 63 | void literalNotDuration() { 64 | 65 | Assertions.assertThatThrownBy(() -> Arguments.checkDuration("x", "duration")) 66 | .isInstanceOf(IllegalArgumentException.class) 67 | .hasMessage("Expecting a duration string for duration. But got: x"); 68 | } 69 | 70 | @Test 71 | void notRequiredValid() { 72 | 73 | Arguments.checkDurationNotRequired(null, "duration"); 74 | Arguments.checkDurationNotRequired("", "duration"); 75 | Arguments.checkDurationNotRequired("1s", "duration"); 76 | } 77 | 78 | @Test 79 | void notRequiredNotValid() { 80 | 81 | Assertions.assertThatThrownBy(() -> Arguments.checkDurationNotRequired("x", "duration")) 82 | .isInstanceOf(IllegalArgumentException.class) 83 | .hasMessage("Expecting a duration string for duration. But got: x"); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/test/java/com/influxdb/v3/client/AbstractMockServerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client; 23 | 24 | import java.io.IOException; 25 | import java.util.HashMap; 26 | import java.util.Map; 27 | import javax.annotation.Nonnull; 28 | import javax.annotation.Nullable; 29 | 30 | import mockwebserver3.MockResponse; 31 | import mockwebserver3.MockWebServer; 32 | import org.junit.jupiter.api.AfterEach; 33 | import org.junit.jupiter.api.BeforeEach; 34 | 35 | public abstract class AbstractMockServerTest { 36 | 37 | protected String baseURL; 38 | protected MockWebServer mockServer; 39 | 40 | @BeforeEach 41 | protected void startMockServer() { 42 | 43 | mockServer = new MockWebServer(); 44 | try { 45 | mockServer.start(); 46 | } catch (IOException e) { 47 | throw new RuntimeException(e); 48 | } 49 | 50 | baseURL = mockServer.url("/").url().toString(); 51 | } 52 | 53 | @AfterEach 54 | protected void shutdownMockServer() throws IOException { 55 | mockServer.close(); 56 | } 57 | 58 | @Nonnull 59 | protected MockResponse createEmptyResponse(final int responseCode) { 60 | return new MockResponse.Builder().code(responseCode).build(); 61 | } 62 | 63 | @Nonnull 64 | protected MockResponse createResponse(final int responseCode, 65 | @Nullable final Map headers, 66 | @Nullable final String body) { 67 | 68 | MockResponse.Builder mrb = new MockResponse.Builder(); 69 | mrb.code(responseCode); 70 | Map effectiveHeaders = new HashMap<>(Map.of("Content-Type", "text/csv; charset=utf-8", 71 | "Date", "Tue, 26 Jun 2018 13:15:01 GMT")); 72 | if (headers != null) { 73 | effectiveHeaders.putAll(headers); 74 | } 75 | for (Map.Entry entry : effectiveHeaders.entrySet()) { 76 | mrb.addHeader(entry.getKey(), entry.getValue()); 77 | } 78 | if (body != null) { 79 | mrb.body(body); 80 | } 81 | 82 | return mrb.build(); 83 | } 84 | 85 | @Nonnull 86 | protected MockResponse createResponse(final int responseCode) { 87 | 88 | return createResponse(responseCode, Map.of( 89 | "Content-Type", "text/csv; charset=utf-8", 90 | "Date", "Tue, 26 Jun 2018 13:15:01 GMT" 91 | ), null); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/com/influxdb/v3/client/InfluxDBApiHttpException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client; 23 | 24 | import java.net.http.HttpHeaders; 25 | import java.util.List; 26 | import javax.annotation.Nullable; 27 | 28 | /** 29 | * The InfluxDBApiHttpException gets thrown whenever an error status is returned 30 | * in the HTTP response. It facilitates recovering from such errors whenever possible. 31 | */ 32 | public class InfluxDBApiHttpException extends InfluxDBApiException { 33 | 34 | /** 35 | * The HTTP headers associated with the error. 36 | */ 37 | HttpHeaders headers; 38 | /** 39 | * The HTTP status code associated with the error. 40 | */ 41 | int statusCode; 42 | 43 | /** 44 | * Construct a new InfluxDBApiHttpException with statusCode and headers. 45 | * 46 | * @param message the detail message. 47 | * @param headers headers returned in the response. 48 | * @param statusCode statusCode of the response. 49 | */ 50 | public InfluxDBApiHttpException( 51 | @Nullable final String message, 52 | @Nullable final HttpHeaders headers, 53 | final int statusCode) { 54 | super(message); 55 | this.headers = headers; 56 | this.statusCode = statusCode; 57 | } 58 | 59 | /** 60 | * Construct a new InfluxDBApiHttpException with statusCode and headers. 61 | * 62 | * @param cause root cause of the exception. 63 | * @param headers headers returned in the response. 64 | * @param statusCode status code of the response. 65 | */ 66 | public InfluxDBApiHttpException( 67 | @Nullable final Throwable cause, 68 | @Nullable final HttpHeaders headers, 69 | final int statusCode) { 70 | super(cause); 71 | this.headers = headers; 72 | this.statusCode = statusCode; 73 | } 74 | 75 | /** 76 | * Gets the HTTP headers property associated with the error. 77 | * 78 | * @return - the headers object. 79 | */ 80 | public HttpHeaders headers() { 81 | return headers; 82 | } 83 | 84 | /** 85 | * Helper method to simplify retrieval of specific headers. 86 | * 87 | * @param name - name of the header. 88 | * @return - value matching the header key, or null if the key does not exist. 89 | */ 90 | public List getHeader(final String name) { 91 | return headers.map().get(name); 92 | } 93 | 94 | /** 95 | * Gets the HTTP statusCode associated with the error. 96 | * @return - the HTTP statusCode. 97 | */ 98 | public int statusCode() { 99 | return statusCode; 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /examples/envoy.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # The MIT License 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | # 22 | 23 | static_resources: 24 | listeners: 25 | - name: listener_0 26 | address: 27 | socket_address: { address: 0.0.0.0, port_value: 10000 } 28 | filter_chains: 29 | - filter_chain_match: 30 | filters: 31 | - name: envoy.filters.network.http_connection_manager 32 | typed_config: 33 | "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager 34 | stat_prefix: ingress_http 35 | access_log: 36 | - name: envoy.access_loggers.stdout 37 | typed_config: 38 | "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog 39 | http2_protocol_options: 40 | allow_connect: true 41 | upgrade_configs: 42 | - upgrade_type: CONNECT 43 | http_filters: 44 | - name: envoy.filters.http.router 45 | typed_config: 46 | "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router 47 | 48 | route_config: 49 | name: local_route 50 | virtual_hosts: 51 | - name: local_service 52 | domains: [ "*" ] 53 | routes: 54 | - match: 55 | connect_matcher: { } 56 | route: 57 | cluster: influxdb_cluster 58 | upgrade_configs: 59 | upgrade_type: CONNECT 60 | connect_config: { } 61 | - match: 62 | prefix: "/" 63 | route: 64 | cluster: influxdb_cluster 65 | prefix_rewrite: "/" 66 | auto_host_rewrite: true 67 | timeout: 10s 68 | cors: 69 | allow_origin_string_match: 70 | - prefix: "*" 71 | allow_methods: GET, PUT, DELETE, POST, OPTIONS 72 | clusters: 73 | - name: influxdb_cluster 74 | connect_timeout: 10s 75 | type: STRICT_DNS 76 | load_assignment: 77 | cluster_name: influxdb_cluster 78 | endpoints: 79 | - lb_endpoints: 80 | - endpoint: 81 | address: 82 | socket_address: 83 | address: "us-east-1-1.aws.cloud2.influxdata.com" 84 | port_value: 443 -------------------------------------------------------------------------------- /scripts/influxdb-setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # The MIT License 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | # THE SOFTWARE. 22 | # 23 | 24 | set -e 25 | 26 | DEFAULT_INFLUXDB_DATABASE=my-db 27 | INFLUXDB_DATABASE="${INFLUXDB_DATABASE:-$DEFAULT_INFLUXDB_DATABASE}" 28 | 29 | # 30 | # Parse command line arguments 31 | # 32 | EXPORT_URL_ENV_VAR="" 33 | EXPORT_DB_ENV_VAR="" 34 | EXPORT_TOKEN_ENV_VAR="" 35 | while [[ $# -gt 0 ]]; do 36 | case $1 in 37 | --export-url-as) 38 | EXPORT_URL_ENV_VAR="$2" 39 | shift 2 40 | ;; 41 | --export-db-as) 42 | EXPORT_DB_ENV_VAR="$2" 43 | shift 2 44 | ;; 45 | --export-token-as) 46 | EXPORT_TOKEN_ENV_VAR="$2" 47 | shift 2 48 | ;; 49 | *) 50 | echo "Unknown option $1" 51 | exit 1 52 | ;; 53 | esac 54 | done 55 | 56 | # 57 | # Check prerequisites 58 | # 59 | for cmd in curl jq; do 60 | command -v ${cmd} &>/dev/null || { echo "'${cmd}' is not installed"; exit 1; } 61 | done 62 | 63 | echo 64 | echo "Wait to start InfluxDB 3.0" 65 | echo 66 | for i in {1..30}; do 67 | if curl -s -o /dev/null -w "%{http_code}" http://localhost:8181/ping | grep -q "401"; then 68 | break 69 | fi 70 | echo "Attempt $i/30: Waiting for InfluxDB to respond with 401..." 71 | sleep 1 72 | done 73 | echo "Done" 74 | 75 | echo 76 | echo "Create admin token" 77 | echo 78 | ADMIN_TOKEN=$(curl -s -X POST http://localhost:8181/api/v3/configure/token/admin | jq -r '.token') 79 | if [[ -z "$ADMIN_TOKEN" || "$ADMIN_TOKEN" == "null" ]]; then 80 | echo "Failed to create admin token" 81 | exit 1 82 | fi 83 | echo "ADMIN_TOKEN=$ADMIN_TOKEN" 84 | 85 | echo 86 | echo "Test the token" 87 | echo 88 | HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer ${ADMIN_TOKEN}" http://localhost:8181/ping) 89 | if [[ "$HTTP_CODE" != "200" ]]; then 90 | echo "Token test failed with HTTP $HTTP_CODE" 91 | exit 1 92 | fi 93 | echo "Done" 94 | 95 | echo 96 | echo "Create database" 97 | echo 98 | HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" -X POST "http://localhost:8181/api/v3/configure/database" \ 99 | -H "Authorization: Bearer ${ADMIN_TOKEN}" \ 100 | -H "Content-Type: application/json" \ 101 | -d "{\"db\":\"${INFLUXDB_DATABASE}\"}") 102 | if [[ "$HTTP_CODE" != "200" ]]; then 103 | echo "Database creation failed with HTTP $HTTP_CODE" 104 | exit 1 105 | fi 106 | echo "Done" 107 | 108 | # 109 | # Export results 110 | # 111 | 112 | export_var() { 113 | local var_name="$1" var_value="$2" 114 | [[ -n "$var_name" ]] || return 115 | [[ -n "$BASH_ENV" ]] || { echo "\$BASH_ENV not available (not in CircleCI), cannot export variables."; exit 1; } 116 | echo "Exporting $var_name=$var_value" 117 | echo "export $var_name=$var_value" >> "$BASH_ENV" 118 | } 119 | 120 | echo 121 | export_var "$EXPORT_URL_ENV_VAR" "http://localhost:8181" 122 | export_var "$EXPORT_DB_ENV_VAR" "$INFLUXDB_DATABASE" 123 | export_var "$EXPORT_TOKEN_ENV_VAR" "$ADMIN_TOKEN" 124 | 125 | -------------------------------------------------------------------------------- /examples/src/main/java/com/influxdb/v3/TimeoutsExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3; 23 | 24 | import java.time.Duration; 25 | import java.util.concurrent.TimeUnit; 26 | import java.util.stream.Stream; 27 | 28 | import com.influxdb.v3.client.InfluxDBClient; 29 | import com.influxdb.v3.client.PointValues; 30 | import com.influxdb.v3.client.config.ClientConfig; 31 | 32 | /** 33 | * This example shows how to set universal timeouts for writes and queries. 34 | *

35 | * The example depends on the "influxdb3-java" module and this module should be built first 36 | * by running "mvn install" in the root directory. 37 | */ 38 | public final class TimeoutsExample { 39 | 40 | public static String resolveProperty(final String property, final String fallback) { 41 | return System.getProperty(property, System.getenv(property)) == null 42 | ? fallback : System.getProperty(property, System.getenv(property)); 43 | } 44 | 45 | private TimeoutsExample() { } 46 | 47 | public static void main(final String[] args) { 48 | // timeout to use for writes. Experiment with lower values to see timeout exceptions. 49 | Duration writeTimeout = Duration.ofMillis(5000L); 50 | // timeout to use for queries. Experiment with lower values to see timeout exceptions. 51 | Duration queryTimeout = Duration.ofMillis(5000L); 52 | 53 | String host = resolveProperty("INFLUX_HOST", "http://localhost:8181"); 54 | String token = resolveProperty("INFLUX_TOKEN", "my-token"); 55 | String database = resolveProperty("INFLUX_DATABASE", "my-database"); 56 | 57 | String measurement = "timeout_example"; 58 | 59 | ClientConfig config = new ClientConfig.Builder() 60 | .host(host) 61 | .token(token.toCharArray()) 62 | .database(database) 63 | .writeTimeout(writeTimeout) // set timeout to be used with the Write API 64 | .queryTimeout(queryTimeout) // set timeout to be used with the Query API 65 | .build(); 66 | 67 | try (InfluxDBClient client = InfluxDBClient.getInstance(config)) { 68 | client.writeRecord(String.format("%s,id=0001 temp=30.14,ticks=42i", measurement)); 69 | 70 | TimeUnit.SECONDS.sleep(1); 71 | String sql = String.format("SELECT * FROM %s ORDER BY time DESC", measurement); 72 | try (Stream values = client.queryPoints(sql)) { 73 | values.forEach(pv -> { 74 | String sv = measurement + "," 75 | + " id: " + pv.getTag("id") + "," 76 | + " fVal: " + pv.getFloatField("temp") + "," 77 | + " iVal: " + pv.getIntegerField("ticks") + "," 78 | + " " + pv.getTimestamp(); 79 | System.out.println(sv); 80 | }); 81 | } 82 | } catch (Exception e) { 83 | throw new RuntimeException(e); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /examples/pom.xml: -------------------------------------------------------------------------------- 1 | 24 | 26 | 4.0.0 27 | 28 | com.influxdb.v3 29 | examples 30 | 1.0-SNAPSHOT 31 | jar 32 | 33 | 34 | UTF-8 35 | com.influxdb.v3.IOxExample 36 | 37 | 38 | 39 | 40 | com.influxdb 41 | influxdb3-java 42 | 1.8.0-SNAPSHOT 43 | 44 | 45 | 46 | 47 | 48 | 49 | org.codehaus.mojo 50 | exec-maven-plugin 51 | 1.2.1 52 | 53 | 54 | 55 | java 56 | 57 | 58 | 59 | 60 | ${exec.main} 61 | 62 | 63 | 64 | org.apache.maven.plugins 65 | maven-compiler-plugin 66 | 3.10.1 67 | 68 | 11 69 | 70 | 71 | 72 | 73 | org.apache.maven.plugins 74 | maven-checkstyle-plugin 75 | 3.2.1 76 | 77 | true 78 | ../checkstyle.xml 79 | true 80 | false 81 | 82 | src/main/java 83 | 84 | 85 | 86 | 87 | verify 88 | 89 | checkstyle 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /src/test/java/com/influxdb/v3/client/TestUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client; 23 | 24 | import java.net.URI; 25 | import java.nio.charset.StandardCharsets; 26 | import java.util.ArrayList; 27 | import java.util.List; 28 | import javax.annotation.Nonnull; 29 | 30 | import org.apache.arrow.flight.FlightServer; 31 | import org.apache.arrow.flight.Location; 32 | import org.apache.arrow.flight.NoOpFlightProducer; 33 | import org.apache.arrow.flight.Ticket; 34 | import org.apache.arrow.memory.BufferAllocator; 35 | import org.apache.arrow.memory.RootAllocator; 36 | import org.apache.arrow.vector.VarCharVector; 37 | import org.apache.arrow.vector.VectorSchemaRoot; 38 | import org.apache.arrow.vector.types.pojo.ArrowType; 39 | import org.apache.arrow.vector.types.pojo.Field; 40 | import org.apache.arrow.vector.types.pojo.FieldType; 41 | import org.apache.arrow.vector.types.pojo.Schema; 42 | 43 | public final class TestUtils { 44 | 45 | private TestUtils() { 46 | throw new IllegalStateException("Utility class"); 47 | } 48 | 49 | public static FlightServer simpleFlightServer(@Nonnull final URI uri, 50 | @Nonnull final BufferAllocator allocator, 51 | @Nonnull final NoOpFlightProducer producer) throws Exception { 52 | Location location = Location.forGrpcInsecure(uri.getHost(), uri.getPort()); 53 | return FlightServer.builder(allocator, location, producer).build(); 54 | } 55 | 56 | public static NoOpFlightProducer simpleProducer(@Nonnull final VectorSchemaRoot vectorSchemaRoot) { 57 | return new NoOpFlightProducer() { 58 | @Override 59 | public void getStream(final CallContext context, 60 | final Ticket ticket, 61 | final ServerStreamListener listener) { 62 | listener.start(vectorSchemaRoot); 63 | if (listener.isReady()) { 64 | listener.putNext(); 65 | } 66 | listener.completed(); 67 | } 68 | }; 69 | } 70 | 71 | public static VectorSchemaRoot generateVectorSchemaRoot(final int fieldCount, final int rowCount) { 72 | List fields = new ArrayList<>(); 73 | for (int i = 0; i < fieldCount; i++) { 74 | Field field = new Field("field" + i, FieldType.nullable(new ArrowType.Utf8()), null); 75 | fields.add(field); 76 | } 77 | 78 | Schema schema = new Schema(fields); 79 | VectorSchemaRoot vectorSchemaRoot = VectorSchemaRoot.create(schema, new RootAllocator(Long.MAX_VALUE)); 80 | for (Field field : fields) { 81 | VarCharVector vector = (VarCharVector) vectorSchemaRoot.getVector(field); 82 | vector.allocateNew(rowCount); 83 | for (int i = 0; i < rowCount; i++) { 84 | vector.set(i, "Value".getBytes(StandardCharsets.UTF_8)); 85 | } 86 | } 87 | vectorSchemaRoot.setRowCount(rowCount); 88 | 89 | return vectorSchemaRoot; 90 | } 91 | } 92 | 93 | -------------------------------------------------------------------------------- /src/test/java/com/influxdb/v3/client/internal/NanosecondConverterTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client.internal; 23 | 24 | import java.math.BigInteger; 25 | 26 | import org.apache.arrow.vector.types.TimeUnit; 27 | import org.apache.arrow.vector.types.pojo.ArrowType; 28 | import org.apache.arrow.vector.types.pojo.Field; 29 | import org.apache.arrow.vector.types.pojo.FieldType; 30 | import org.junit.jupiter.api.Assertions; 31 | import org.junit.jupiter.api.Test; 32 | 33 | public class NanosecondConverterTest { 34 | 35 | @Test 36 | void testGetTimestampNanosecond() { 37 | BigInteger timestampNanoSecond = null; 38 | 39 | // Second 40 | FieldType timeTypeSecond = new FieldType(true, 41 | new ArrowType.Timestamp(TimeUnit.SECOND, "UTC"), 42 | null); 43 | Field timeFieldSecond = new Field("time", timeTypeSecond, null); 44 | timestampNanoSecond = NanosecondConverter.getTimestampNano(123_456L, timeFieldSecond); 45 | Assertions.assertEquals( 46 | BigInteger.valueOf(123_456L) 47 | .multiply(BigInteger.valueOf(1_000_000_000)), timestampNanoSecond 48 | ); 49 | 50 | // MilliSecond 51 | FieldType timeTypeMilliSecond = new FieldType(true, 52 | new ArrowType.Timestamp(TimeUnit.MILLISECOND, "UTC"), 53 | null); 54 | Field timeFieldMilliSecond = new Field("time", timeTypeMilliSecond, null); 55 | timestampNanoSecond = NanosecondConverter.getTimestampNano(123_456L, timeFieldMilliSecond); 56 | Assertions.assertEquals( 57 | BigInteger.valueOf(123_456L) 58 | .multiply(BigInteger.valueOf(1_000_000)), timestampNanoSecond 59 | ); 60 | 61 | // MicroSecond 62 | FieldType timeTypeMicroSecond = new FieldType(true, 63 | new ArrowType.Timestamp(TimeUnit.MICROSECOND, "UTC"), 64 | null); 65 | Field timeFieldMicroSecond = new Field("time", timeTypeMicroSecond, null); 66 | timestampNanoSecond = NanosecondConverter.getTimestampNano(123_456L, timeFieldMicroSecond); 67 | Assertions.assertEquals( 68 | BigInteger.valueOf(123_456L) 69 | .multiply(BigInteger.valueOf(1_000)), timestampNanoSecond 70 | ); 71 | 72 | // Nano Second 73 | FieldType timeTypeNanoSecond = new FieldType(true, 74 | new ArrowType.Timestamp(TimeUnit.NANOSECOND, "UTC"), 75 | null); 76 | Field timeFieldNanoSecond = new Field("time", timeTypeNanoSecond, null); 77 | timestampNanoSecond = NanosecondConverter.getTimestampNano(123_456L, timeFieldNanoSecond); 78 | Assertions.assertEquals(BigInteger.valueOf(123_456L), timestampNanoSecond); 79 | 80 | // For ArrowType.Time type 81 | FieldType timeMilliSecond = new FieldType(true, 82 | new ArrowType.Time(TimeUnit.MILLISECOND, 32), 83 | null); 84 | Field fieldMilliSecond = new Field("time", timeMilliSecond, null); 85 | timestampNanoSecond = NanosecondConverter.getTimestampNano(123_456L, fieldMilliSecond); 86 | Assertions.assertEquals( 87 | BigInteger.valueOf(123_456L) 88 | .multiply(BigInteger.valueOf(1_000_000)), timestampNanoSecond 89 | ); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /examples/src/main/java/com/influxdb/v3/DownsamplingExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3; 23 | 24 | import java.time.Instant; 25 | import java.time.LocalDateTime; 26 | import java.time.ZoneOffset; 27 | import java.time.temporal.ChronoUnit; 28 | import java.util.stream.Stream; 29 | 30 | import com.influxdb.v3.client.InfluxDBClient; 31 | import com.influxdb.v3.client.Point; 32 | import com.influxdb.v3.client.PointValues; 33 | 34 | /** 35 | * The example depends on the "influxdb3-java" module and this module should be built first 36 | * by running "mvn install" in the root directory. 37 | */ 38 | public final class DownsamplingExample { 39 | private DownsamplingExample() { 40 | } 41 | 42 | public static void main(final String[] args) throws Exception { 43 | String host = "https://us-east-1-1.aws.cloud2.influxdata.com"; 44 | String token = "my-token"; 45 | String database = "my-database"; 46 | 47 | try (InfluxDBClient client = InfluxDBClient.getInstance(host, token.toCharArray(), database)) { 48 | // 49 | // Write data 50 | // 51 | Point point1 = Point.measurement("stat") 52 | .setTag("unit", "temperature") 53 | .setField("avg", 24.5) 54 | .setField("max", 45.0) 55 | .setTimestamp(Instant.now().minus(20, ChronoUnit.MINUTES)); 56 | client.writePoint(point1); 57 | 58 | Point point2 = Point.measurement("stat") 59 | .setTag("unit", "temperature") 60 | .setField("avg", 28.0) 61 | .setField("max", 40.3) 62 | .setTimestamp(Instant.now().minus(10, ChronoUnit.MINUTES)); 63 | client.writePoint(point2); 64 | 65 | Point point3 = Point.measurement("stat") 66 | .setTag("unit", "temperature") 67 | .setField("avg", 20.5) 68 | .setField("max", 49.0) 69 | .setTimestamp(Instant.now()); 70 | client.writePoint(point3); 71 | 72 | // 73 | // Query downsampled data 74 | // 75 | String sql = "SELECT\n" 76 | + " date_bin('5 minutes', \"time\") as window_start,\n" 77 | + " AVG(\"avg\") as avg,\n" 78 | + " MAX(\"max\") as max\n" 79 | + " FROM \"stat\"\n" 80 | + " WHERE\n" 81 | + " \"time\" >= now() - interval '1 hour'\n" 82 | + " GROUP BY window_start\n" 83 | + " ORDER BY window_start ASC;\n"; 84 | 85 | 86 | // 87 | // Execute downsampling query into pointValues 88 | // 89 | try (Stream stream = client.queryPoints(sql)) { 90 | stream.forEach( 91 | (PointValues row) -> { 92 | var timestamp = row.getField("window_start", LocalDateTime.class); 93 | 94 | if (timestamp == null) { 95 | return; 96 | } 97 | 98 | System.out.printf("%s: avg is %s, max is %s%n", 99 | timestamp, row.getFloatField("avg"), row.getFloatField("max")); 100 | 101 | // 102 | // write back downsampled date to 'stat_downsampled' measurement 103 | // 104 | var downsampledPoint = row 105 | .asPoint("stat_downsampled") 106 | .removeField("window_start") 107 | .setTimestamp(timestamp.toInstant(ZoneOffset.UTC)); 108 | 109 | client.writePoint(downsampledPoint); 110 | }); 111 | } 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/test/java/com/influxdb/v3/client/InfluxDBClientTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client; 23 | 24 | import java.util.Map; 25 | import java.util.Properties; 26 | 27 | import org.assertj.core.api.Assertions; 28 | import org.junit.jupiter.api.Test; 29 | 30 | import com.influxdb.v3.client.config.ClientConfig; 31 | 32 | public class InfluxDBClientTest { 33 | 34 | @Test 35 | void withProxyUrl() { 36 | String proxyUrl = "http://localhost:10000"; 37 | ClientConfig.Builder builder = new ClientConfig.Builder(); 38 | builder.proxyUrl(proxyUrl); 39 | ClientConfig clientConfig = builder.build(); 40 | Assertions.assertThat(clientConfig.getProxyUrl()).isEqualTo(proxyUrl); 41 | } 42 | 43 | @Test 44 | void withSslRootsFilePath() { 45 | String path = "/path/to/cert"; 46 | ClientConfig.Builder builder = new ClientConfig.Builder(); 47 | builder.sslRootsFilePath(path); 48 | ClientConfig clientConfig = builder.build(); 49 | Assertions.assertThat(clientConfig.sslRootsFilePath()).isEqualTo(path); 50 | } 51 | 52 | @Test 53 | void requiredHost() { 54 | 55 | Assertions.assertThatThrownBy(() -> InfluxDBClient.getInstance(null, "my-token".toCharArray(), "my-database")) 56 | .isInstanceOf(IllegalArgumentException.class) 57 | .hasMessage("The URL of the InfluxDB server has to be defined."); 58 | } 59 | 60 | @Test 61 | void requiredHostConnectionString() { 62 | 63 | Assertions.assertThatThrownBy(() -> InfluxDBClient.getInstance("?token=my-token&database=my-database")) 64 | .isInstanceOf(IllegalArgumentException.class) 65 | .hasMessageContaining("no protocol"); 66 | } 67 | 68 | @Test 69 | void fromParameters() throws Exception { 70 | 71 | try (InfluxDBClient client = InfluxDBClient.getInstance("http://localhost:8086", 72 | "my-token".toCharArray(), "my-database")) { 73 | Assertions.assertThat(client).isNotNull(); 74 | } 75 | } 76 | 77 | @Test 78 | void fromConnectionString() throws Exception { 79 | 80 | try (InfluxDBClient client = InfluxDBClient.getInstance("http://localhost:8086" 81 | + "?token=my-token&database=my-db")) { 82 | Assertions.assertThat(client).isNotNull(); 83 | } 84 | } 85 | 86 | @Test 87 | void fromEnvOrProperties() throws Exception { 88 | 89 | final Properties old = System.getProperties(); 90 | final Properties p = new Properties(); 91 | p.put("influx.host", "http://localhost:8086"); 92 | p.put("influx.token", "my-token"); 93 | p.put("influx.database", "my-db"); 94 | System.setProperties(p); 95 | 96 | try (InfluxDBClient client = InfluxDBClient.getInstance()) { 97 | Assertions.assertThat(client).isNotNull(); 98 | } finally { 99 | System.setProperties(old); 100 | } 101 | } 102 | 103 | @Test 104 | void withDefaultTags() throws Exception { 105 | 106 | Map defaultTags = Map.of("unit", "U2", "model", "M1"); 107 | 108 | try (InfluxDBClient client = InfluxDBClient.getInstance( 109 | "http://localhost:8086", 110 | "MY-TOKEN".toCharArray(), 111 | "MY-DATABASE", 112 | defaultTags)) { 113 | Assertions.assertThat(client).isNotNull(); 114 | } 115 | } 116 | 117 | @Test 118 | public void unsupportedQueryParams() throws Exception { 119 | try (InfluxDBClient client = InfluxDBClient.getInstance("http://localhost:8086", 120 | "my-token".toCharArray(), "my-database")) { 121 | 122 | String query = "select * from cpu where client=$client"; 123 | Map parameters = Map.of("client", client); 124 | 125 | Assertions.assertThatThrownBy(() -> client.queryPoints(query, parameters)) 126 | .isInstanceOf(IllegalArgumentException.class) 127 | .hasMessage("The parameter client value has unsupported type: " 128 | + "class com.influxdb.v3.client.internal.InfluxDBClientImpl"); 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /examples/src/main/java/com/influxdb/v3/IOxExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3; 23 | 24 | import java.time.Instant; 25 | import java.util.Map; 26 | import java.util.stream.Stream; 27 | 28 | import com.influxdb.v3.client.InfluxDBClient; 29 | import com.influxdb.v3.client.Point; 30 | import com.influxdb.v3.client.PointValues; 31 | import com.influxdb.v3.client.query.QueryOptions; 32 | 33 | /** 34 | * The example depends on the "influxdb3-java" module and this module should be built first 35 | * by running "mvn install" in the root directory. 36 | */ 37 | public final class IOxExample { 38 | private IOxExample() { 39 | } 40 | 41 | public static void main(final String[] args) throws Exception { 42 | String host = "https://us-east-1-1.aws.cloud2.influxdata.com"; 43 | String token = "my-token"; 44 | String database = "my-database"; 45 | 46 | try (InfluxDBClient client = InfluxDBClient.getInstance(host, token.toCharArray(), database)) { 47 | 48 | // 49 | // Write by Point 50 | // 51 | Point point = Point.measurement("temperature") 52 | .setTag("location", "west") 53 | .setField("value", 55.15) 54 | .setTimestamp(Instant.now().minusSeconds(-10)); 55 | client.writePoint(point); 56 | 57 | // 58 | // Write by LineProtocol 59 | // 60 | String record = "temperature,location=north value=60.0"; 61 | client.writeRecord(record); 62 | 63 | // 64 | // Query by SQL 65 | // 66 | System.out.printf("--------------------------------------------------------%n"); 67 | System.out.printf("| %-8s | %-8s | %-30s |%n", "location", "value", "time"); 68 | System.out.printf("--------------------------------------------------------%n"); 69 | 70 | String sql = "select time,location,value from temperature order by time desc limit 10"; 71 | try (Stream stream = client.query(sql)) { 72 | stream.forEach(row -> System.out.printf("| %-8s | %-8s | %-30s |%n", row[1], row[2], row[0])); 73 | } 74 | 75 | System.out.printf("--------------------------------------------------------%n%n"); 76 | 77 | // 78 | // Query by parametrized SQL 79 | // 80 | System.out.printf("--------------------Parametrized SQL--------------------%n"); 81 | System.out.printf("| %-8s | %-8s | %-30s |%n", "location", "value", "time"); 82 | System.out.printf("--------------------------------------------------------%n"); 83 | 84 | String sqlWithParameters = 85 | "select time,location,value from temperature where location=$location order by time desc limit 10"; 86 | try (Stream stream = client.query(sqlWithParameters, Map.of("location", "north"))) { 87 | stream.forEach(row -> System.out.printf("| %-8s | %-8s | %-30s |%n", row[1], row[2], row[0])); 88 | } 89 | 90 | System.out.printf("--------------------------------------------------------%n%n"); 91 | 92 | // 93 | // Query by InfluxQL 94 | // 95 | System.out.printf("-----------------------------------------%n"); 96 | System.out.printf("| %-16s | %-18s |%n", "time", "mean"); 97 | System.out.printf("-----------------------------------------%n"); 98 | 99 | String influxQL = 100 | "select MEAN(value) from temperature group by time(1d) fill(none) order by time desc limit 10"; 101 | try (Stream stream = client.query(influxQL, QueryOptions.defaultInfluxQlQueryOptions())) { 102 | stream.forEach(row -> System.out.printf("| %-16s | %-18s |%n", row[1], row[2])); 103 | } 104 | 105 | System.out.printf("-----------------------------------------%n%n"); 106 | 107 | 108 | System.out.printf("--------------------------------------------------------%n"); 109 | System.out.printf("| %-8s | %-8s | %-30s |%n", "location", "value", "time"); 110 | System.out.printf("--------------------------------------------------------%n"); 111 | 112 | // 113 | // Query by SQL into Points 114 | // 115 | try (Stream stream = client.queryPoints(sql)) { 116 | stream.forEach( 117 | (PointValues p) -> { 118 | var time = p.getTimestamp(); 119 | var location = p.getTag("location"); 120 | var value = p.getField("value", Double.class); 121 | 122 | System.out.printf("| %-8s | %-8s | %-30s |%n", location, value, time); 123 | }); 124 | } 125 | 126 | System.out.printf("--------------------------------------------------------%n%n"); 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/test/java/com/influxdb/v3/client/internal/ArgumentsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client.internal; 23 | 24 | import java.time.temporal.ChronoUnit; 25 | 26 | import org.assertj.core.api.Assertions; 27 | import org.junit.jupiter.api.Test; 28 | 29 | /** 30 | * @author Jakub Bednar (bednar@github) (01/08/2018 15:29) 31 | */ 32 | @SuppressWarnings("ConstantConditions") 33 | class ArgumentsTest { 34 | 35 | @Test 36 | void checkNonEmptyString() { 37 | 38 | Arguments.checkNonEmpty("valid", "property"); 39 | } 40 | 41 | @Test 42 | void checkNonEmptyStringEmpty() { 43 | 44 | Assertions.assertThatThrownBy(() -> Arguments.checkNonEmpty("", "property")) 45 | .isInstanceOf(IllegalArgumentException.class) 46 | .hasMessage("Expecting a non-empty string for property"); 47 | } 48 | 49 | @Test 50 | void checkNonEmptyStringNull() { 51 | 52 | Assertions.assertThatThrownBy(() -> Arguments.checkNonEmpty(null, "property")) 53 | .isInstanceOf(IllegalArgumentException.class) 54 | .hasMessage("Expecting a non-empty string for property"); 55 | } 56 | 57 | @Test 58 | void checkPositiveNumber() { 59 | 60 | Arguments.checkPositiveNumber(10, "property"); 61 | } 62 | 63 | @Test 64 | void checkPositiveNumberNull() { 65 | 66 | Assertions.assertThatThrownBy(() -> Arguments.checkPositiveNumber(null, "property")) 67 | .isInstanceOf(IllegalArgumentException.class) 68 | .hasMessage("Expecting a positive number for property"); 69 | } 70 | 71 | @Test 72 | void checkPositiveNumberZero() { 73 | 74 | Assertions.assertThatThrownBy(() -> Arguments.checkPositiveNumber(0, "property")) 75 | .isInstanceOf(IllegalArgumentException.class) 76 | .hasMessage("Expecting a positive number for property"); 77 | } 78 | 79 | @Test 80 | void checkPositiveNumberZeroNegative() { 81 | 82 | Assertions.assertThatThrownBy(() -> Arguments.checkPositiveNumber(-12L, "property")) 83 | .isInstanceOf(IllegalArgumentException.class) 84 | .hasMessage("Expecting a positive number for property"); 85 | } 86 | 87 | @Test 88 | void checkNotNegativeNumber() { 89 | 90 | Arguments.checkNotNegativeNumber(0, "valid"); 91 | } 92 | 93 | @Test 94 | void checkNotNegativeNumberNull() { 95 | 96 | Assertions.assertThatThrownBy(() -> Arguments.checkNotNegativeNumber(null, "property")) 97 | .isInstanceOf(IllegalArgumentException.class) 98 | .hasMessage("Expecting a positive or zero number for property"); 99 | } 100 | 101 | @Test 102 | void checkNotNegativeNumberNegative() { 103 | 104 | Assertions.assertThatThrownBy(() -> Arguments.checkNotNegativeNumber(-12L, "property")) 105 | .isInstanceOf(IllegalArgumentException.class) 106 | .hasMessage("Expecting a positive or zero number for property"); 107 | } 108 | 109 | @Test 110 | void checkOneCharString() { 111 | 112 | Arguments.checkOneCharString("#", "valid"); 113 | } 114 | 115 | @Test 116 | void checkOneCharStringEmpty() { 117 | 118 | Assertions.assertThatThrownBy(() -> Arguments.checkOneCharString("", "property")) 119 | .isInstanceOf(IllegalArgumentException.class) 120 | .hasMessage("Expecting a one char string for property"); 121 | } 122 | 123 | @Test 124 | void checkOneCharStringNull() { 125 | 126 | Assertions.assertThatThrownBy(() -> Arguments.checkOneCharString(null, "property")) 127 | .isInstanceOf(IllegalArgumentException.class) 128 | .hasMessage("Expecting a one char string for property"); 129 | } 130 | 131 | @Test 132 | void checkOneCharStringLarge() { 133 | 134 | Assertions.assertThatThrownBy(() -> Arguments.checkOneCharString("##", "property")) 135 | .isInstanceOf(IllegalArgumentException.class) 136 | .hasMessage("Expecting a one char string for property"); 137 | } 138 | 139 | @Test 140 | void checkNotNull() { 141 | Arguments.checkNotNull("value", "property"); 142 | } 143 | 144 | @Test 145 | void checkNotNullFail() { 146 | Assertions.assertThatThrownBy(() -> Arguments.checkNotNull(null, "property")) 147 | .isInstanceOf(NullPointerException.class) 148 | .hasMessage("Expecting a not null reference for property"); 149 | } 150 | 151 | @Test 152 | void checkPrecision() { 153 | 154 | Arguments.checkPrecision(ChronoUnit.SECONDS); 155 | } 156 | 157 | @Test 158 | void checkPrecisionNotSupported() { 159 | 160 | Assertions.assertThatThrownBy(() -> Arguments.checkPrecision(ChronoUnit.DAYS)) 161 | .isInstanceOf(IllegalArgumentException.class) 162 | .hasMessage("Precision must be one of: [Nanos, Micros, Millis, Seconds]"); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /examples/src/main/java/com/influxdb/v3/durable/InfluxClientPool.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.durable; 23 | 24 | import java.util.ArrayList; 25 | import java.util.List; 26 | import java.util.Stack; 27 | import java.util.logging.Logger; 28 | 29 | import com.influxdb.v3.client.InfluxDBClient; 30 | import com.influxdb.v3.client.config.ClientConfig; 31 | 32 | /** 33 | * And example pool for InfluxDBClient clients. 34 | *

35 | * All clients handled by the pool will share the same basic configuration. 36 | */ 37 | public class InfluxClientPool implements AutoCloseable { 38 | 39 | Logger logger = Logger.getLogger(InfluxClientPool.class.getName()); 40 | 41 | private static final int DEFAULT_MAX_SIZE = 4; 42 | 43 | // Container for clients waiting to be used. 44 | Stack idlers = new Stack<>(); 45 | // Container for clients currently in use. 46 | List runners = new ArrayList<>(); 47 | 48 | int maxSize; 49 | 50 | // The shared configuration. 51 | final ClientConfig clientConfig; 52 | 53 | /** 54 | * Basic Constructor, uses DEFAULT_MAX_SIZE for maxSize. 55 | *

56 | * @param clientConfig - the standard configuration for all clients managed by the pool. 57 | */ 58 | public InfluxClientPool(final ClientConfig clientConfig) { 59 | this(clientConfig, DEFAULT_MAX_SIZE); 60 | } 61 | 62 | /** 63 | * Basic constructor. 64 | *

65 | * @param clientConfig - the standard configuration for all clients managed by the pool. 66 | */ 67 | public InfluxClientPool(final ClientConfig clientConfig, final int maxSize) { 68 | this.clientConfig = clientConfig; 69 | this.maxSize = maxSize; 70 | } 71 | 72 | /** 73 | * Checks for a free client in the idle stack. If the idle stack 74 | * is empty, it generates a new client. 75 | * 76 | * @return - An InfluxDBClient ready for use. 77 | */ 78 | public synchronized InfluxDBClient borrowClient() { 79 | InfluxDBClient client; 80 | if (idlers.isEmpty()) { 81 | client = InfluxDBClient.getInstance(clientConfig); 82 | runners.add(client); 83 | if (activeCount() >= maxSize) { 84 | // N.B. this is just an example implementation. 85 | // For simplicity this _example_ will allow the maxSize value to be exceeded with a severe warning. 86 | // In a production environment maxSize should be managed appropriately. 87 | logger.severe("Max pool size " + maxSize + " exceeded: " + "actives " 88 | + activeCount() + " idles " + idleCount() 89 | + " (hint: Is there a process hogging zombie clients?)"); 90 | } 91 | } else { 92 | client = idlers.pop(); 93 | runners.add(client); 94 | } 95 | logger.info("Lending client " + client.hashCode()); 96 | return client; 97 | } 98 | 99 | /** 100 | * Invalidate a client if some unwanted exception state is encountered, 101 | * or if for some other reason it is unusable or no longer needed. 102 | * 103 | * @param client - client to be closed and flagged for garbage collection. 104 | */ 105 | public synchronized void invalidateClient(final InfluxDBClient client) { 106 | runners.remove(client); 107 | try { 108 | client.close(); 109 | } catch (Exception e) { 110 | logger.warning("Exception occurred when invalidating client " 111 | + client.hashCode() + ": " + e.getMessage()); 112 | } 113 | } 114 | 115 | /** 116 | * Return the client to the idle stack within the pool 117 | * when it is no longer needed but can still be reused. 118 | * 119 | * @param client - the client to be returned. 120 | */ 121 | public synchronized void returnClient(final InfluxDBClient client) { 122 | logger.info("Returning client " + client.hashCode()); 123 | runners.remove(client); 124 | idlers.push(client); 125 | } 126 | 127 | /** 128 | * Handle closing all resources. 129 | *

130 | * First return active clients to the idle stack. 131 | * Then close all clients in the idle stack. 132 | * 133 | * @throws Exception 134 | */ 135 | @Override 136 | public synchronized void close() throws Exception { 137 | logger.info("Closing client pool"); 138 | int stillActiveCount = activeCount(); 139 | for (int i = stillActiveCount - 1; i >= 0; i--) { 140 | returnClient(runners.get(i)); 141 | } 142 | while (!idlers.isEmpty()) { 143 | try (InfluxDBClient client = idlers.pop()) { 144 | logger.info("Closing client " + client.hashCode()); 145 | } catch (IllegalStateException e) { 146 | StringBuilder msg = new StringBuilder("IllegalStateException when closing client. "); 147 | msg.append(e.getMessage()); 148 | if (e.getMessage().contains("leaked")) { 149 | msg.append(" (hint: were all streams returned from queries closed correctly?)"); 150 | } 151 | logger.warning(msg.toString()); 152 | } catch (Exception e) { // client close should be automatic 153 | logger.warning("Exception when closing client " + e.getMessage()); 154 | } 155 | } 156 | } 157 | 158 | public synchronized int activeCount() { 159 | return runners.size(); 160 | } 161 | 162 | public synchronized int idleCount() { 163 | return idlers.size(); 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /examples/src/main/java/com/influxdb/v3/durable/Sensor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.durable; 23 | 24 | import java.time.Instant; 25 | 26 | import com.influxdb.v3.client.Point; 27 | 28 | /** 29 | * A generic Sensor. 30 | */ 31 | public class Sensor { 32 | 33 | long id; 34 | String name; 35 | String model; 36 | String location; 37 | 38 | public Sensor(final String name, final String model, final String location) { 39 | this.id = Math.round(Math.random() * 1000000); 40 | this.name = name; 41 | this.model = model; 42 | this.location = location; 43 | } 44 | 45 | public long getId() { 46 | return id; 47 | } 48 | 49 | public String getName() { 50 | return name; 51 | } 52 | 53 | public String getModel() { 54 | return model; 55 | } 56 | 57 | public String getLocation() { 58 | return location; 59 | } 60 | 61 | @Override 62 | public String toString() { 63 | return String.format("[%d] name: %s model: %s location: %s", id, name, model, location); 64 | } 65 | 66 | /** 67 | * Creates random data for illustrative or testing purposes only. 68 | * 69 | * @param timestamp - a timestamp for the data point. If null returns now. 70 | * @return A Sensor.DataPoint 71 | */ 72 | public Sensor.DataPoint randomPoint(final Instant timestamp) { 73 | return new Sensor.DataPoint(this, randomAccel(), randomVel(), randomBearing(), 74 | randomAccel(), randomVel(), randomBearing(), timestamp); 75 | } 76 | 77 | /** 78 | * Creates a random data point with Instant.now(). 79 | * 80 | * @return A Sensor.DataPoint. 81 | */ 82 | public Sensor.DataPoint randomPoint() { 83 | return randomPoint(Instant.now()); 84 | } 85 | 86 | private static double randomAccel() { 87 | return Math.random(); 88 | } 89 | 90 | private static double randomVel() { 91 | return Math.random() + Math.random(); 92 | } 93 | 94 | private static double randomBearing() { 95 | return Math.random() * 360.0; 96 | } 97 | 98 | 99 | public static class DataPoint { 100 | Sensor sensor; 101 | 102 | double hAccel; 103 | double hVel; 104 | double hBearing; 105 | double vAccel; 106 | double vVel; 107 | double vBearing; 108 | Instant timestamp; 109 | 110 | String lpFormat = "%s,id=%s,location=%s,model=%s,name=%s " 111 | + "hAccel=%.6f,hBearing=%.6f,hVel=%.6f,vAccel=%.6f,vBearing=%.6f,vVel=%.6f %d"; 112 | 113 | /** 114 | * Used for illustrating client fail over when incorrect line protocol values are sent. 115 | */ 116 | String lpFormatBroken = "%s,id=%s,location=%s,model=%s,name=%s " 117 | + "hAccel=,hBearing=%.6f,hVel=%.6f,vAccel=%.6f,vBearing=%.6f,vVel=%.6f %d"; 118 | 119 | 120 | public DataPoint(final Sensor sensor, 121 | final double hAccel, 122 | final double hVel, 123 | final double hBearing, 124 | final double vAccel, 125 | final double vVel, 126 | final double vBearing, 127 | final Instant timestamp) { 128 | this.sensor = sensor; 129 | this.hAccel = hAccel; 130 | this.hVel = hVel; 131 | this.hBearing = hBearing; 132 | this.vAccel = vAccel; 133 | this.vVel = vVel; 134 | this.vBearing = vBearing; 135 | this.timestamp = timestamp == null ? Instant.now() : timestamp; 136 | } 137 | 138 | @Override 139 | public String toString() { 140 | 141 | return String.format("%s - hAccel: %.3f, hVel: %.3f, hBearing: %.3f, vAccel: %.3f, vVel: %.3f, vBearing: %.3f %s", 142 | sensor, hAccel, hVel, hBearing, vAccel, vVel, vBearing, timestamp); 143 | } 144 | 145 | /** 146 | * Converts a Sensor.DataPoint to an InfluxDBClient v3 Point. 147 | * 148 | * @return - an InfluxDBClient v3 Point. 149 | */ 150 | public Point toPoint() { 151 | return Point.measurement(sensor.getClass().getSimpleName().toLowerCase()) 152 | .setTag("name", sensor.getName()) 153 | .setTag("model", sensor.getModel()) 154 | .setTag("location", sensor.getLocation()) 155 | .setTag("id", Long.toString(sensor.getId())) 156 | .setFloatField("hAccel", hAccel) 157 | .setFloatField("hVel", hVel) 158 | .setFloatField("hBearing", hBearing) 159 | .setFloatField("vAccel", vAccel) 160 | .setFloatField("vVel", vVel) 161 | .setFloatField("vBearing", vBearing) 162 | .setTimestamp(timestamp); 163 | } 164 | 165 | /** 166 | * Convert the Sensor.DataPoint to a valid Line protocol string. 167 | * 168 | * @return - a valid Line protocol string. 169 | */ 170 | public String toLP() { 171 | long nanos = timestamp.getEpochSecond() * 1_000_000_000 + timestamp.getNano(); 172 | return String.format(lpFormat, 173 | sensor.getClass().getSimpleName().toLowerCase(), 174 | sensor.getId(), 175 | sensor.getLocation(), 176 | sensor.getModel(), 177 | sensor.getName(), 178 | hAccel, 179 | hBearing, 180 | hVel, 181 | vAccel, 182 | vBearing, 183 | vVel, 184 | nanos 185 | ); 186 | } 187 | 188 | /** 189 | * To be used to illustrate how InfluxDBClient recovers from 190 | * submitting incorrect data to InfluxDB. 191 | * 192 | * @return - an invalid Line protocol string. 193 | */ 194 | public String toLPBroken() { 195 | long nanos = timestamp.getEpochSecond() * 1_000_000_000 + timestamp.getNano(); 196 | return String.format(lpFormatBroken, 197 | sensor.getClass().getSimpleName().toLowerCase(), 198 | sensor.getId(), 199 | sensor.getLocation(), 200 | sensor.getModel(), 201 | sensor.getName(), 202 | hBearing, 203 | hVel, 204 | vAccel, 205 | vBearing, 206 | vVel, 207 | nanos 208 | ); 209 | } 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /src/main/java/com/influxdb/v3/client/internal/Arguments.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client.internal; 23 | 24 | import java.time.temporal.ChronoUnit; 25 | import java.util.EnumSet; 26 | import java.util.Objects; 27 | import java.util.regex.Pattern; 28 | import javax.annotation.Nullable; 29 | 30 | /** 31 | * Functions for parameter validation. 32 | *

33 | * Thanks 34 | */ 35 | public final class Arguments { 36 | 37 | private static final Pattern DURATION_PATTERN = Pattern.compile("([-+]?)([0-9]+(\\.[0-9]*)?[a-z]+)+|inf|-inf", 38 | Pattern.CASE_INSENSITIVE); 39 | 40 | private static final String DURATION_MESSAGE = "Expecting a duration string for %s. But got: %s"; 41 | 42 | /** 43 | * The precisions that are allowed to use in the writes. 44 | */ 45 | private static final EnumSet ALLOWED_PRECISION = EnumSet.of(ChronoUnit.NANOS, 46 | ChronoUnit.MICROS, ChronoUnit.MILLIS, ChronoUnit.SECONDS); 47 | 48 | private Arguments() { 49 | } 50 | 51 | /** 52 | * Enforces that the string is {@linkplain String#isEmpty() not empty}. 53 | * 54 | * @param string the string to test 55 | * @param name variable name for reporting 56 | * @return {@code string} 57 | * @throws IllegalArgumentException if the string is empty 58 | */ 59 | public static String checkNonEmpty(final String string, final String name) throws IllegalArgumentException { 60 | if (string == null || string.isEmpty()) { 61 | throw new IllegalArgumentException("Expecting a non-empty string for " + name); 62 | } 63 | return string; 64 | } 65 | 66 | /** 67 | * Enforces that the string has exactly one char. 68 | * 69 | * @param string the string to test 70 | * @param name variable name for reporting 71 | * @return {@code string} 72 | * @throws IllegalArgumentException if the string has not one char 73 | */ 74 | public static String checkOneCharString(final String string, final String name) throws IllegalArgumentException { 75 | if (string == null || string.length() != 1) { 76 | throw new IllegalArgumentException("Expecting a one char string for " + name); 77 | } 78 | return string; 79 | } 80 | 81 | /** 82 | * Enforces that the string is duration literal. 83 | * 84 | * @param string the string to test 85 | * @param name variable name for reporting 86 | * @return {@code string} 87 | * @throws IllegalArgumentException if the string is not duration literal 88 | */ 89 | public static String checkDuration(final String string, final String name) throws IllegalArgumentException { 90 | if (string == null || string.isEmpty() || !DURATION_PATTERN.matcher(string).matches()) { 91 | throw new IllegalArgumentException(String.format(DURATION_MESSAGE, name, string)); 92 | } 93 | 94 | return string; 95 | } 96 | 97 | /** 98 | * Enforces that the string is duration literal. Empty or null strings are valid. 99 | * 100 | * @param string the string to test 101 | * @param name variable name for reporting 102 | * @return {@code string} 103 | * @throws IllegalArgumentException if the string is not duration literal 104 | */ 105 | public static String checkDurationNotRequired(final String string, final String name) 106 | throws IllegalArgumentException { 107 | if (string != null && !string.isEmpty() && !DURATION_PATTERN.matcher(string).matches()) { 108 | throw new IllegalArgumentException(String.format(DURATION_MESSAGE, name, string)); 109 | } 110 | 111 | return string; 112 | } 113 | 114 | /** 115 | * Enforces that the number is larger than 0. 116 | * 117 | * @param number the number to test 118 | * @param name variable name for reporting 119 | * @throws IllegalArgumentException if the number is less or equal to 0 120 | */ 121 | public static void checkPositiveNumber(final Number number, final String name) throws IllegalArgumentException { 122 | if (number == null || number.doubleValue() <= 0) { 123 | throw new IllegalArgumentException("Expecting a positive number for " + name); 124 | } 125 | } 126 | 127 | /** 128 | * Enforces that the number is not negative. 129 | * 130 | * @param number the number to test 131 | * @param name variable name for reporting 132 | * @throws IllegalArgumentException if the number is less or equal to 0 133 | */ 134 | public static void checkNotNegativeNumber(final Number number, final String name) throws IllegalArgumentException { 135 | if (number == null || number.doubleValue() < 0) { 136 | throw new IllegalArgumentException("Expecting a positive or zero number for " + name); 137 | } 138 | } 139 | 140 | /** 141 | * Checks that the specified object reference is not {@code null}. 142 | * 143 | * @param obj the object reference to check for nullity 144 | * @param name variable name for reporting 145 | * @throws NullPointerException if the object is {@code null} 146 | * @see Objects#requireNonNull(Object, String) 147 | */ 148 | public static void checkNotNull(final Object obj, final String name) throws NullPointerException { 149 | 150 | Objects.requireNonNull(obj, () -> "Expecting a not null reference for " + name); 151 | } 152 | 153 | /** 154 | * Checks that the precision reference to one of {@link Arguments#ALLOWED_PRECISION}. 155 | * 156 | * @param precision the precision to check 157 | * @throws IllegalArgumentException if the object is not one of {@link Arguments#ALLOWED_PRECISION} 158 | */ 159 | public static void checkPrecision(@Nullable final ChronoUnit precision) throws IllegalArgumentException { 160 | 161 | if (!ALLOWED_PRECISION.contains(precision)) { 162 | throw new IllegalArgumentException("Precision must be one of: " + ALLOWED_PRECISION); 163 | } 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /src/main/java/com/influxdb/v3/client/internal/NanosecondConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client.internal; 23 | 24 | import java.math.BigDecimal; 25 | import java.math.BigInteger; 26 | import java.time.Instant; 27 | import java.time.LocalDateTime; 28 | import java.time.ZoneOffset; 29 | import java.util.HashMap; 30 | import java.util.Map; 31 | import java.util.concurrent.TimeUnit; 32 | import java.util.function.Function; 33 | import javax.annotation.Nonnull; 34 | import javax.annotation.Nullable; 35 | 36 | import org.apache.arrow.vector.types.pojo.ArrowType; 37 | import org.apache.arrow.vector.types.pojo.Field; 38 | 39 | import com.influxdb.v3.client.write.WritePrecision; 40 | 41 | import static java.util.function.Function.identity; 42 | 43 | /** 44 | * Nanosecond converter. 45 | *

46 | * Utility class converting epoch nanoseconds to epoch with a given precision. 47 | */ 48 | public final class NanosecondConverter { 49 | 50 | private static final BigInteger NANOS_PER_SECOND = BigInteger.valueOf(1000_000_000L); 51 | private static final BigInteger MICRO_PER_NANOS = BigInteger.valueOf(1000L); 52 | private static final BigInteger MILLIS_PER_NANOS = BigInteger.valueOf(1000_000L); 53 | private static final BigInteger SECONDS_PER_NANOS = BigInteger.valueOf(1000_000_000L); 54 | 55 | private NanosecondConverter() { 56 | } 57 | 58 | /** 59 | * Timestamp calculation functions to add timestamp to records. 60 | */ 61 | private static final Map> FROM_NANOS = new HashMap<>(); 62 | 63 | static { 64 | FROM_NANOS.put(WritePrecision.S, (timestamp) -> timestamp.divide(SECONDS_PER_NANOS)); 65 | FROM_NANOS.put(WritePrecision.MS, (timestamp) -> timestamp.divide(MILLIS_PER_NANOS)); 66 | FROM_NANOS.put(WritePrecision.US, (timestamp) -> timestamp.divide(MICRO_PER_NANOS)); 67 | FROM_NANOS.put(WritePrecision.NS, identity()); 68 | } 69 | 70 | /** 71 | * Timestamp calculation functions from specified precision to nanos. 72 | */ 73 | private static final Map> TO_NANOS = new HashMap<>(); 74 | 75 | static { 76 | TO_NANOS.put(WritePrecision.S, (timestamp) -> timestamp.multiply(SECONDS_PER_NANOS)); 77 | TO_NANOS.put(WritePrecision.MS, (timestamp) -> timestamp.multiply(MILLIS_PER_NANOS)); 78 | TO_NANOS.put(WritePrecision.US, (timestamp) -> timestamp.multiply(MICRO_PER_NANOS)); 79 | TO_NANOS.put(WritePrecision.NS, identity()); 80 | } 81 | 82 | /** 83 | * Convert timestamp in a given precision to nanoseconds. 84 | * 85 | * @param timestamp epoch timestamp 86 | * @param precision precision 87 | * @return epoch timestamp in precision, can be null 88 | */ 89 | @Nullable 90 | public static BigInteger convertToNanos(@Nullable final Number timestamp, final WritePrecision precision) { 91 | if (timestamp == null) { 92 | return null; 93 | } 94 | 95 | BigInteger t; 96 | if (timestamp instanceof BigDecimal) { 97 | t = ((BigDecimal) timestamp).toBigInteger(); 98 | } else if (timestamp instanceof BigInteger) { 99 | t = (BigInteger) timestamp; 100 | } else { 101 | t = BigInteger.valueOf(timestamp.longValue()); 102 | } 103 | 104 | return TO_NANOS.get(precision).apply(t); 105 | } 106 | 107 | /** 108 | * Convert {@link Instant} timestamp to a given precision. 109 | * 110 | * @param instant Instant timestamp 111 | * @param precision precision 112 | * @return epoch timestamp in precision 113 | */ 114 | public static BigInteger convert(final Instant instant, final WritePrecision precision) { 115 | BigInteger nanos = BigInteger.valueOf(instant.getEpochSecond()) 116 | .multiply(NANOS_PER_SECOND) 117 | .add(BigInteger.valueOf(instant.getNano())); 118 | 119 | return FROM_NANOS.get(precision).apply(nanos); 120 | } 121 | 122 | /** 123 | * Convert Long or LocalDateTime to timestamp nanosecond. 124 | * 125 | * @param value the time in Long or LocalDateTime 126 | * @param field the arrow field metadata 127 | * @return the time in nanosecond 128 | */ 129 | @Nullable 130 | public static BigInteger getTimestampNano(@Nonnull final Object value, @Nonnull final Field field) { 131 | BigInteger result = null; 132 | 133 | if (value instanceof Long) { 134 | if (field.getFieldType().getType() instanceof ArrowType.Timestamp) { 135 | ArrowType.Timestamp type = (ArrowType.Timestamp) field.getFieldType().getType(); 136 | TimeUnit timeUnit; 137 | switch (type.getUnit()) { 138 | case SECOND: 139 | timeUnit = TimeUnit.SECONDS; 140 | break; 141 | case MILLISECOND: 142 | timeUnit = TimeUnit.MILLISECONDS; 143 | break; 144 | case MICROSECOND: 145 | timeUnit = TimeUnit.MICROSECONDS; 146 | break; 147 | case NANOSECOND: 148 | default: 149 | timeUnit = TimeUnit.NANOSECONDS; 150 | break; 151 | } 152 | long nanoseconds = TimeUnit.NANOSECONDS.convert((Long) value, timeUnit); 153 | Instant instant = Instant.ofEpochSecond(0, nanoseconds); 154 | result = convertInstantToNano(instant); 155 | } else { 156 | Instant instant = Instant.ofEpochMilli((Long) value); 157 | result = convertInstantToNano(instant); 158 | } 159 | } else if (value instanceof LocalDateTime) { 160 | Instant instant = ((LocalDateTime) value).toInstant(ZoneOffset.UTC); 161 | result = convertInstantToNano(instant); 162 | } 163 | return result; 164 | } 165 | 166 | @Nullable 167 | private static BigInteger convertInstantToNano(@Nonnull final Instant instant) { 168 | var writePrecision = WritePrecision.NS; 169 | BigInteger convertedTime = NanosecondConverter.convert(instant, writePrecision); 170 | return NanosecondConverter.convertToNanos(convertedTime, writePrecision); 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /checkstyle.xml: -------------------------------------------------------------------------------- 1 | 2 | 25 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Duke 3 |

4 |

5 | 6 | Maven Central Badge 7 | 8 | 9 | Maven Site 10 | 11 | 12 | CodeQL analysis 13 | 14 | 15 | Lint Code Base 16 | 17 | 18 | CircleCI 19 | 20 | 21 | Code Cov 22 | 23 | 24 | Community Slack 25 | 26 |

27 | 28 | # InfluxDB 3 Java Client 29 | 30 | The Java client that provides an easy and convenient way to interact with InfluxDB 3. 31 | This package supports both writing data to InfluxDB and querying data using the FlightSQL client, 32 | which allows you to execute SQL queries against InfluxDB IOx. 33 | 34 | We offer this [Getting Started: InfluxDB 3.0 Java Client Library](https://www.youtube.com/watch?v=EFnG7rUDvR4) video for learning more about the library. 35 | 36 | > :warning: This client requires Java 11 and is compatible up to and including Java 25. 37 | 38 | ## Installation 39 | 40 | > :warning: Some JDK internals must be exposed by adding `--add-opens=java.base/java.nio=ALL-UNNAMED` to your JVM arguments. 41 | 42 | If you are using JDK 25 or later, you also need to add `--sun-misc-unsafe-memory-access=allow`. 43 | 44 | Add the latest version of the client to your project: 45 | 46 | ### Maven dependency 47 | 48 | ```xml 49 | 50 | com.influxdb 51 | influxdb3-java 52 | 1.7.0 53 | 54 | ``` 55 | 56 | ### Or when using Gradle 57 | 58 | ```groovy 59 | dependencies { 60 | implementation "com.influxdb:influxdb3-java:1.7.0" 61 | } 62 | ``` 63 | 64 | ## Usage 65 | 66 | To start with the client, import the `com.influxdb.v3.client` package and create a `InfluxDBClient` by: 67 | 68 | ```java 69 | package com.influxdb.v3; 70 | 71 | import java.time.Instant; 72 | import java.util.stream.Stream; 73 | 74 | import com.influxdb.v3.client.InfluxDBClient; 75 | import com.influxdb.v3.client.query.QueryOptions; 76 | import com.influxdb.v3.client.Point; 77 | 78 | public class IOxExample { 79 | public static void main(String[] args) throws Exception { 80 | String host = "https://us-east-1-1.aws.cloud2.influxdata.com"; 81 | char[] token = "my-token".toCharArray(); 82 | String database = "database"; 83 | 84 | try (InfluxDBClient client = InfluxDBClient.getInstance(host, token, database)) { 85 | // ... 86 | } 87 | } 88 | } 89 | ``` 90 | 91 | to insert data, you can use code like this: 92 | 93 | ```java 94 | // 95 | // Write by Point 96 | // 97 | Point point = Point.measurement("temperature") 98 | .setTag("location", "west") 99 | .setField("value", 55.15) 100 | .setTimestamp(Instant.now().minusSeconds(-10)); 101 | client.writePoint(point); 102 | 103 | // 104 | // Write by LineProtocol 105 | // 106 | String record = "temperature,location=north value=60.0"; 107 | client.writeRecord(record); 108 | ``` 109 | 110 | to query your data, you can use code like this: 111 | 112 | ```java 113 | // 114 | // Query by SQL 115 | // 116 | System.out.printf("--------------------------------------------------------%n"); 117 | System.out.printf("| %-8s | %-8s | %-30s |%n", "location", "value", "time"); 118 | System.out.printf("--------------------------------------------------------%n"); 119 | 120 | String sql = "select time,location,value from temperature order by time desc limit 10"; 121 | try (Stream stream = client.query(sql)) { 122 | stream.forEach(row -> System.out.printf("| %-8s | %-8s | %-30s |%n", row[1], row[2], row[0])); 123 | } 124 | 125 | System.out.printf("--------------------------------------------------------%n%n"); 126 | 127 | // 128 | // Query by parametrized SQL 129 | // 130 | System.out.printf("--------------------Parametrized SQL--------------------%n"); 131 | System.out.printf("| %-8s | %-8s | %-30s |%n", "location", "value", "time"); 132 | System.out.printf("--------------------------------------------------------%n"); 133 | 134 | String sqlParams = "select time,location,value from temperature where location=$location order by time desc limit 10"; 135 | try (Stream stream = client.query(sqlParams, Map.of("location", "north"))) { 136 | stream.forEach(row -> System.out.printf("| %-8s | %-8s | %-30s |%n", row[1], row[2], row[0])); 137 | } 138 | 139 | System.out.printf("--------------------------------------------------------%n%n"); 140 | 141 | // 142 | // Query by InfluxQL 143 | // 144 | System.out.printf("-----------------------------------------%n"); 145 | System.out.printf("| %-16s | %-18s |%n", "time", "mean"); 146 | System.out.printf("-----------------------------------------%n"); 147 | 148 | String influxQL = "select MEAN(value) from temperature group by time(1d) fill(none) order by time desc limit 10"; 149 | try (Stream stream = client.query(influxQL, QueryOptions.INFLUX_QL)) { 150 | stream.forEach(row -> System.out.printf("| %-16s | %-18s |%n", row[1], row[2])); 151 | } 152 | 153 | System.out.printf("-----------------------------------------%n"); 154 | ``` 155 | 156 | or use `PointValues` structure with `client.queryPoints`: 157 | 158 | ```java 159 | System.out.printf("--------------------------------------------------------%n"); 160 | System.out.printf("| %-8s | %-8s | %-30s |%n", "location", "value", "time"); 161 | System.out.printf("--------------------------------------------------------%n"); 162 | 163 | // 164 | // Query by SQL into Points 165 | // 166 | String sql1 = "select time,location,value from temperature order by time desc limit 10"; 167 | try (Stream stream = client.queryPoints(sql1, QueryOptions.DEFAULTS)) { 168 | stream.forEach( 169 | (PointValues p) -> { 170 | var time = p.getTimestamp(); 171 | var location = p.getTag("location"); 172 | var value = p.getField("value", Double.class); 173 | 174 | System.out.printf("| %-8s | %-8s | %-30s |%n", location, value, time); 175 | }); 176 | } 177 | 178 | System.out.printf("--------------------------------------------------------%n%n"); 179 | ``` 180 | 181 | ## gRPC Compression 182 | 183 | The Java client supports gRPC response compression. 184 | 185 | If the server chooses to compress query responses (e.g., with gzip), the client 186 | will automatically decompress them — no extra configuration is required. 187 | 188 | ## Feedback 189 | 190 | If you need help, please use our [Community Slack](https://app.slack.com/huddle/TH8RGQX5Z/C02UDUPLQKA) 191 | or [Community Page](https://community.influxdata.com/). 192 | 193 | New features and bugs can be reported on GitHub: 194 | 195 | ## Contribution 196 | 197 | If you would like to contribute code you can do through GitHub by forking the repository and sending a pull request into 198 | the `main` branch. 199 | 200 | ## License 201 | 202 | The InfluxDB 3 Java Client is released under the [MIT License](https://opensource.org/licenses/MIT). 203 | -------------------------------------------------------------------------------- /src/test/java/com/influxdb/v3/client/issues/MemoryLeakIssueTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client.issues; 23 | 24 | import java.util.concurrent.CountDownLatch; 25 | import java.util.concurrent.TimeUnit; 26 | import java.util.concurrent.atomic.AtomicInteger; 27 | import java.util.logging.Logger; 28 | import java.util.stream.Stream; 29 | 30 | import org.junit.jupiter.api.Assertions; 31 | import org.junit.jupiter.api.Test; 32 | import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; 33 | 34 | import com.influxdb.v3.client.InfluxDBClient; 35 | import com.influxdb.v3.client.PointValues; 36 | import com.influxdb.v3.client.config.ClientConfig; 37 | 38 | public class MemoryLeakIssueTest { 39 | 40 | private static final Logger LOG = Logger.getLogger(MemoryLeakIssueTest.class.getName()); 41 | 42 | /** 43 | * Tests that interrupting a thread during stream consumption does not cause Arrow memory leaks. 44 | *

45 | * This test creates a query thread that slowly consumes results, then interrupts it mid-processing. 46 | * The interrupt causes FlightStream.close() to throw InterruptedException, which previously bypassed 47 | * cleanup code and left Apache Arrow buffers unreleased. With the fix, client.close() should complete 48 | * successfully without throwing "Memory was leaked" errors. 49 | */ 50 | @EnabledIfEnvironmentVariable(named = "TESTING_INFLUXDB_URL", matches = ".*") 51 | @EnabledIfEnvironmentVariable(named = "TESTING_INFLUXDB_TOKEN", matches = ".*") 52 | @EnabledIfEnvironmentVariable(named = "TESTING_INFLUXDB_DATABASE", matches = ".*") 53 | @Test 54 | void testStreamCloseWithThreadInterrupt() throws Exception { 55 | String host = System.getenv("TESTING_INFLUXDB_URL"); 56 | String token = System.getenv("TESTING_INFLUXDB_TOKEN"); 57 | String database = System.getenv("TESTING_INFLUXDB_DATABASE"); 58 | String measurement = "memory_leak_test_" + System.currentTimeMillis(); 59 | String sql = String.format("SELECT * FROM %s", measurement); 60 | 61 | // Prepare config 62 | ClientConfig config = new ClientConfig.Builder() 63 | .host(host) 64 | .token(token.toCharArray()) 65 | .database(database) 66 | .writeNoSync(true) 67 | .build(); 68 | 69 | try (InfluxDBClient client = InfluxDBClient.getInstance(config)) { 70 | // Write test data 71 | LOG.info("Writing test data..."); 72 | for (int i = 0; i < 3; i++) { 73 | client.writeRecord(String.format("%s,id=%04d temp=%f", 74 | measurement, i, 20.0 + Math.random() * 10)); 75 | } 76 | 77 | // Wait for data to be queryable (CI environments can be slower) 78 | LOG.info("Waiting for data to be available..."); 79 | int attempts = 0; 80 | boolean hasData = false; 81 | while (attempts < 10 && !hasData) { 82 | try (Stream testStream = client.queryPoints(sql)) { 83 | hasData = testStream.findFirst().isPresent(); 84 | } 85 | if (!hasData) { 86 | LOG.info("Data not yet available, waiting... (attempt " + (attempts + 1) + "/10)"); 87 | TimeUnit.MILLISECONDS.sleep(500); 88 | attempts++; 89 | } 90 | } 91 | 92 | if (!hasData) { 93 | Assertions.fail("No data available after writing and waiting " + (attempts * 500) + "ms"); 94 | } 95 | LOG.info("Data is available, starting test..."); 96 | 97 | } 98 | 99 | // Query data 100 | InfluxDBClient client = InfluxDBClient.getInstance(config); 101 | //noinspection TryFinallyCanBeTryWithResources 102 | try { 103 | // Synchronization to ensure we interrupt during consumption 104 | CountDownLatch consumingStarted = new CountDownLatch(1); 105 | AtomicInteger rowsProcessed = new AtomicInteger(0); 106 | AtomicInteger exceptionsThrown = new AtomicInteger(0); 107 | 108 | Thread queryThread = new Thread(() -> { 109 | try (Stream stream = client.queryPoints(sql)) { 110 | LOG.info("queryPoints returned"); 111 | stream.forEach(pv -> { 112 | int count = rowsProcessed.incrementAndGet(); 113 | 114 | // Signal that we've started consuming 115 | if (count == 1) { 116 | LOG.info("Started consuming - ready for interrupt"); 117 | consumingStarted.countDown(); 118 | } 119 | 120 | try { 121 | // Slow consumption to ensure we're mid-stream when interrupted 122 | TimeUnit.MILLISECONDS.sleep(100); 123 | } catch (InterruptedException e) { 124 | LOG.info("INTERRUPTED during consume! (after " + count + " rows)"); 125 | Thread.currentThread().interrupt(); 126 | // Throw exception to stop stream consumption immediately; try-with-resources will then 127 | // close stream in interrupted state 128 | throw new RuntimeException("Interrupted", e); 129 | } 130 | }); 131 | } catch (Exception e) { 132 | exceptionsThrown.incrementAndGet(); 133 | LOG.info("Exception caught: " + e); 134 | } 135 | }); 136 | 137 | queryThread.start(); 138 | 139 | // Wait for thread to start consuming 140 | if (!consumingStarted.await(10, TimeUnit.SECONDS)) { 141 | Assertions.fail("Thread didn't start consuming in time!"); 142 | } 143 | 144 | // Give it a moment to be mid-processing 145 | TimeUnit.MILLISECONDS.sleep(50); 146 | 147 | // Interrupt during processing 148 | LOG.info("Interrupting thread..."); 149 | queryThread.interrupt(); 150 | 151 | // Wait for thread to finish 152 | queryThread.join(10000); 153 | 154 | // Verify that thread started processing rows 155 | if (rowsProcessed.get() == 0) { 156 | Assertions.fail("Thread didn't process any rows"); 157 | } 158 | 159 | // Verify that exception was thrown due to interrupt 160 | if (exceptionsThrown.get() == 0) { 161 | Assertions.fail("No exception was thrown - interrupt might not have worked"); 162 | } 163 | 164 | } catch (Exception e) { 165 | LOG.severe("Test failed: " + e.getMessage()); 166 | throw e; 167 | } finally { 168 | // Now close the client. 169 | // It should not throw `Memory was leaked by query. Memory leaked: (...)` error! 170 | LOG.info("Closing the client..."); 171 | client.close(); 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/main/java/com/influxdb/v3/client/query/QueryOptions.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client.query; 23 | 24 | import java.util.Map; 25 | import java.util.Objects; 26 | import javax.annotation.Nonnull; 27 | import javax.annotation.Nullable; 28 | import javax.annotation.concurrent.ThreadSafe; 29 | 30 | import com.influxdb.v3.client.config.ClientConfig; 31 | import com.influxdb.v3.client.internal.Arguments; 32 | import com.influxdb.v3.client.internal.GrpcCallOptions; 33 | 34 | /** 35 | * Query API options. 36 | *

37 | * Supports to specify: 38 | *

    39 | *
  • database - specifies the database to be used for InfluxDB operations
  • 40 | *
  • queryType - specifies the type of query sent to InfluxDB. Default to 'SQL'.
  • 41 | *
  • headers - specifies the headers to be added to query request
  • 42 | *
43 | *

44 | * To add custom headers to the query request, use the following code: 45 | *

 46 |  * QueryOptions options = new QueryOptions(Map.of("X-Tracing-Id", "123"));
 47 |  * Stream<Object[]> rows = client.query("select * from cpu", queryOptions);
 48 |  * 
49 | */ 50 | @ThreadSafe 51 | @SuppressWarnings("ConstantConditions") 52 | public final class QueryOptions { 53 | 54 | /** 55 | * Default QueryAPI options.
56 | * Deprecated: use {@link #defaultQueryOptions} instead. 57 | */ 58 | @Deprecated(forRemoval = true) 59 | public static final QueryOptions DEFAULTS = new QueryOptions(null, QueryType.SQL); 60 | 61 | /** 62 | * Default QueryAPI options for InfluxQL.
63 | * Deprecated: use {@link #defaultInfluxQlQueryOptions} instead. 64 | */ 65 | @Deprecated(forRemoval = true) 66 | public static final QueryOptions INFLUX_QL = new QueryOptions(null, QueryType.InfluxQL); 67 | 68 | private final String database; 69 | private final QueryType queryType; 70 | private final Map headers; 71 | private GrpcCallOptions grpcCallOptions = GrpcCallOptions.getDefaultOptions(); 72 | 73 | /** 74 | * Provides default query options with no database specified and using SQL as the query type. 75 | * 76 | * @return A {@code QueryOptions} instance with default settings, including a null database 77 | * and {@code QueryType.SQL} as the query type. 78 | */ 79 | public static QueryOptions defaultQueryOptions() { 80 | return new QueryOptions(null, QueryType.SQL); 81 | } 82 | 83 | /** 84 | * Provides default query options for executing InfluxQL queries with no database specified. 85 | * 86 | * @return A {@code QueryOptions} instance configured with a null database and {@code QueryType.InfluxQL}. 87 | */ 88 | public static QueryOptions defaultInfluxQlQueryOptions() { 89 | return new QueryOptions(null, QueryType.InfluxQL); 90 | } 91 | 92 | /** 93 | * Construct QueryAPI options. The query type is set to SQL. 94 | * 95 | * @param database The database to be used for InfluxDB operations. 96 | */ 97 | public QueryOptions(@Nonnull final String database) { 98 | this(database, QueryType.SQL); 99 | } 100 | 101 | /** 102 | * Construct QueryAPI options. 103 | * 104 | * @param queryType The type of query sent to InfluxDB. 105 | */ 106 | public QueryOptions(@Nonnull final QueryType queryType) { 107 | this(null, queryType); 108 | } 109 | 110 | /** 111 | * Construct QueryAPI options. The query type is set to SQL. 112 | * 113 | * @param headers The headers to be added to query request. 114 | * The headers specified here are preferred over the headers specified in the client configuration. 115 | */ 116 | public QueryOptions(@Nullable final Map headers) { 117 | this(null, QueryType.SQL, headers); 118 | } 119 | 120 | /** 121 | * Construct QueryAPI options. 122 | * 123 | * @param database The database to be used for InfluxDB operations. 124 | * If it is not specified then use {@link ClientConfig#getDatabase()}. 125 | * @param queryType The type of query sent to InfluxDB. If it is not specified then use {@link QueryType#SQL}. 126 | */ 127 | public QueryOptions(@Nullable final String database, @Nullable final QueryType queryType) { 128 | this(database, queryType, null); 129 | } 130 | 131 | /** 132 | * Construct QueryAPI options. 133 | * 134 | * @param database The database to be used for InfluxDB operations. 135 | * If it is not specified then use {@link ClientConfig#getDatabase()}. 136 | * @param queryType The type of query sent to InfluxDB. If it is not specified then use {@link QueryType#SQL}. 137 | * @param headers The headers to be added to query request. 138 | * The headers specified here are preferred over the headers specified in the client configuration. 139 | */ 140 | public QueryOptions(@Nullable final String database, 141 | @Nullable final QueryType queryType, 142 | @Nullable final Map headers) { 143 | this.database = database; 144 | this.queryType = queryType; 145 | this.headers = headers == null ? Map.of() : headers; 146 | } 147 | 148 | /** 149 | * @param config with default value 150 | * @return The destination database for writes. 151 | */ 152 | @Nullable 153 | public String databaseSafe(@Nonnull final ClientConfig config) { 154 | Arguments.checkNotNull(config, "config"); 155 | return isNotDefined(database) ? config.getDatabase() : database; 156 | } 157 | 158 | /** 159 | * @return The type of query sent to InfluxDB, cannot be null. 160 | */ 161 | @Nonnull 162 | public QueryType queryTypeSafe() { 163 | return queryType == null ? QueryType.SQL : queryType; 164 | } 165 | 166 | /** 167 | * @return The headers to be added to query request, cannot be null. 168 | */ 169 | @Nonnull 170 | public Map headersSafe() { 171 | return headers; 172 | } 173 | 174 | /** 175 | * Sets the GrpcCallOptions object. 176 | * @param grpcCallOptions the grpcCallOptions 177 | */ 178 | public void setGrpcCallOptions(@Nonnull final GrpcCallOptions grpcCallOptions) { 179 | Arguments.checkNotNull(grpcCallOptions, "grpcCallOptions"); 180 | this.grpcCallOptions = grpcCallOptions; 181 | } 182 | 183 | /** 184 | * @return the GrpcCallOptions object. 185 | */ 186 | @Nonnull 187 | public GrpcCallOptions grpcCallOptions() { 188 | return grpcCallOptions; 189 | } 190 | 191 | private boolean isNotDefined(final String option) { 192 | return option == null || option.isEmpty(); 193 | } 194 | 195 | @Override 196 | public boolean equals(final Object o) { 197 | if (this == o) { 198 | return true; 199 | } 200 | if (o == null || getClass() != o.getClass()) { 201 | return false; 202 | } 203 | QueryOptions that = (QueryOptions) o; 204 | return Objects.equals(this.database, that.database) 205 | && Objects.equals(this.queryType, that.queryType) 206 | && Objects.equals(this.headers, that.headers) 207 | && Objects.equals(this.grpcCallOptions, that.grpcCallOptions); 208 | } 209 | 210 | @Override 211 | public int hashCode() { 212 | return Objects.hash(database, queryType, headers, grpcCallOptions); 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /examples/src/main/java/com/influxdb/v3/RetryExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3; 23 | 24 | import java.time.Instant; 25 | import java.time.LocalDateTime; 26 | import java.time.ZoneOffset; 27 | import java.time.format.DateTimeParseException; 28 | import java.time.temporal.ChronoUnit; 29 | import java.util.ArrayList; 30 | import java.util.List; 31 | import java.util.UUID; 32 | import java.util.concurrent.TimeUnit; 33 | import java.util.logging.Logger; 34 | 35 | import com.influxdb.v3.client.InfluxDBApiHttpException; 36 | import com.influxdb.v3.client.InfluxDBClient; 37 | import com.influxdb.v3.client.Point; 38 | 39 | 40 | /** 41 | * This examples shows how to catch certain HttpExceptions and leverage their status code and header 42 | * values to attempt to rewrite data refused by the server. It attempts to exceed the maximum allowable writes 43 | * for an unpaid Influx cloud account. 44 | *

45 | * A First run with 100,000 records should succeed. but a follow-up run within a couple of minutes 46 | * should result in a refusal with the HTTP status code 429 "Too Many Requests" 47 | *

48 | * See the examples README.md for more information. 49 | */ 50 | public final class RetryExample { 51 | 52 | private static final java.util.logging.Logger LOG = Logger.getLogger(RetryExample.class.getName()); 53 | 54 | private RetryExample() { } 55 | 56 | static int retries = 0; 57 | 58 | public static String resolveProperty(final String property, final String fallback) { 59 | return System.getProperty(property, System.getenv(property)) == null 60 | ? fallback : System.getProperty(property, System.getenv(property)); 61 | } 62 | 63 | public static void main(final String[] args) throws Exception { 64 | 65 | String host = resolveProperty("INFLUX_HOST", "https://us-east-1-1.aws.cloud2.influxdata.com"); 66 | String token = resolveProperty("INFLUX_TOKEN", "my-token"); 67 | String database = resolveProperty("INFLUX_DATABASE", "my-database"); 68 | 69 | Instant now = Instant.now(); 70 | int dataSetSize = 100000; 71 | 72 | LOG.info(String.format("Preparing to write %d records.\n", dataSetSize)); 73 | 74 | List packs = new ArrayList<>(); 75 | List points = new ArrayList<>(); 76 | for (int i = 0; i < dataSetSize; i++) { 77 | packs.add(BatteryPack.createRandom()); 78 | points.add(packs.get(i).toPoint(now.minus((dataSetSize - i) * 10, ChronoUnit.MILLIS))); 79 | } 80 | 81 | retries = 1; 82 | try (InfluxDBClient client = InfluxDBClient.getInstance(host, token.toCharArray(), database)) { 83 | writeWithRetry(client, points, retries); 84 | } 85 | } 86 | 87 | /** 88 | * Resolves the retry value in milliseconds. 89 | * 90 | * @param retryVal - retry value from the HTTP header "retry-after" 91 | * @return - wait interval in milliseconds 92 | */ 93 | private static long resolveRetry(final String retryVal) { 94 | try { 95 | return (Long.parseLong(retryVal) * 1000); 96 | } catch (NumberFormatException nfe) { 97 | try { 98 | Instant now = Instant.now(); 99 | Instant retry = LocalDateTime.parse(retryVal).toInstant(ZoneOffset.UTC); 100 | return retry.toEpochMilli() - now.toEpochMilli(); 101 | } catch (DateTimeParseException dtpe) { 102 | throw new RuntimeException("Unable to parse retry time: " + retryVal, dtpe); 103 | } 104 | } 105 | } 106 | 107 | /** 108 | * Helper method to be called recursively in the event a retry is required. 109 | * 110 | * @param client - InfluxDBClient used to make the request. 111 | * @param points - Data to be written. 112 | * @param retryCount - Number of times to retry write requests. 113 | * @throws InterruptedException - if wait is interrupted. 114 | */ 115 | private static void writeWithRetry(final InfluxDBClient client, 116 | final List points, 117 | final int retryCount) throws InterruptedException { 118 | try { 119 | client.writePoints(points); 120 | LOG.info(String.format("Succeeded on write %d\n", (retries - retryCount) + 1)); 121 | } catch (InfluxDBApiHttpException e) { 122 | if ((e.statusCode() == 429 || e.statusCode() == 503) 123 | && e.getHeader("retry-after") != null 124 | && retryCount > 0) { 125 | long wait = resolveRetry(e.getHeader("retry-after").get(0)) + 1000; 126 | LOG.warning( 127 | String.format("Failed to write with HTTP %d waiting %d secs to retry.\n", e.statusCode(), wait / 1000) 128 | ); 129 | keepBusy(wait); 130 | LOG.info(String.format("Write attempt %d", ((retries - (retryCount - 1)) + 1))); 131 | writeWithRetry(client, points, retryCount - 1); 132 | } else { 133 | LOG.severe(String.format("Failed to write in %d tries. Giving up with HTTP %d\n", 134 | retries, 135 | e.statusCode())); 136 | } 137 | } 138 | } 139 | 140 | /** 141 | * Convenience method for a nicer wait experience. 142 | * 143 | * @param wait interval to wait before retrying. 144 | * @throws InterruptedException in the event wait is interrupted. 145 | */ 146 | private static void keepBusy(final long wait) throws InterruptedException { 147 | long ttl = Instant.now().toEpochMilli() + wait; 148 | String[] spinner = {"-", "\\", "|", "/"}; 149 | int count = 0; 150 | while (Instant.now().toEpochMilli() < ttl) { 151 | System.out.printf("\r%s", spinner[count++ % 4]); 152 | System.out.flush(); 153 | TimeUnit.MILLISECONDS.sleep(500); 154 | } 155 | System.out.println(); 156 | } 157 | 158 | /** 159 | * An example data type roughly modeling battery packs in an EV. 160 | */ 161 | static class BatteryPack { 162 | String id; 163 | String vehicle; 164 | int emptyCells; 165 | int cellRack; 166 | double percent; 167 | 168 | public BatteryPack(final String id, 169 | final String vehicle, 170 | final int emptyCells, 171 | final int cellRack, 172 | final double percent) { 173 | this.id = id; 174 | this.vehicle = vehicle; 175 | this.emptyCells = emptyCells; 176 | this.cellRack = cellRack; 177 | this.percent = percent; 178 | } 179 | 180 | public Point toPoint(final Instant time) { 181 | return Point.measurement("bpack") 182 | .setTag("id", id) 183 | .setField("vehicle", vehicle) 184 | .setField("emptyCells", emptyCells) 185 | .setField("totalCells", cellRack) 186 | .setField("percent", percent) 187 | .setTimestamp(time); 188 | } 189 | 190 | static List idSet = List.of(UUID.randomUUID().toString(), 191 | UUID.randomUUID().toString(), 192 | UUID.randomUUID().toString(), 193 | UUID.randomUUID().toString(), 194 | UUID.randomUUID().toString()); 195 | 196 | static List vehicleSet = List.of( 197 | "THX1138", 198 | "VC81072", 199 | "J007O981", 200 | "NTSL1856", 201 | "TOURDF24" 202 | ); 203 | 204 | static List cellsSet = List.of(100, 200, 500); 205 | 206 | public static BatteryPack createRandom() { 207 | int index = (int) Math.floor(Math.random() * 5); 208 | int totalCells = cellsSet.get((int) Math.floor(Math.random() * 3)); 209 | int emptyCells = (int) Math.floor(Math.random() * totalCells); 210 | double percent = ((((double) emptyCells / (double) totalCells)) * 100); 211 | percent += Math.random() * ((100 - percent) * 0.15); 212 | percent = 100 - percent; 213 | return new BatteryPack(idSet.get(index), vehicleSet.get(index), emptyCells, totalCells, percent); 214 | } 215 | 216 | @Override 217 | public String toString() { 218 | return String.format("id: %s, vehicle: %s, cellRack: %d, emptyCells: %d, percent: %f", 219 | id, vehicle, cellRack, emptyCells, percent); 220 | } 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | parameters: 4 | default-maven-image: 5 | type: string 6 | default: "cimg/openjdk:11.0" 7 | 8 | executors: 9 | docker-amd64-image: 10 | parameters: 11 | maven-image: 12 | type: string 13 | default: << pipeline.parameters.default-maven-image >> 14 | docker: 15 | - image: <> 16 | - image: influxdb:3-core 17 | environment: 18 | - INFLUXDB3_NODE_IDENTIFIER_PREFIX=node01 19 | - INFLUXDB3_OBJECT_STORE=file 20 | - INFLUXDB3_DB_DIR=/var/lib/influxdb3/data 21 | docker-arm64-image: 22 | parameters: 23 | maven-image: 24 | type: string 25 | default: << pipeline.parameters.default-maven-image >> 26 | docker: 27 | - image: <> 28 | - image: influxdb:3-core 29 | environment: 30 | - INFLUXDB3_NODE_IDENTIFIER_PREFIX=node01 31 | - INFLUXDB3_OBJECT_STORE=file 32 | - INFLUXDB3_DB_DIR=/var/lib/influxdb3/data 33 | resource_class: arm.medium 34 | 35 | commands: 36 | upload-codecov-amd64: 37 | steps: 38 | - run: 39 | name: Collecting coverage reports 40 | command: | 41 | curl -Os https://uploader.codecov.io/latest/linux/codecov 42 | curl -Os https://uploader.codecov.io/latest/linux/codecov.SHA256SUM 43 | curl -Os https://uploader.codecov.io/latest/linux/codecov.SHA256SUM.sig 44 | curl -s https://keybase.io/codecovsecurity/pgp_keys.asc | gpg --no-default-keyring --keyring trustedkeys.gpg --import 45 | gpgv codecov.SHA256SUM.sig codecov.SHA256SUM 46 | shasum -a 256 -c codecov.SHA256SUM 47 | chmod +x ./codecov 48 | ./codecov 49 | upload-codecov-arm64: 50 | steps: 51 | - run: 52 | name: Collecting coverage reports 53 | command: | 54 | curl -k https://keybase.io/codecovsecurity/pgp_keys.asc | gpg --no-default-keyring --keyring trustedkeys.gpg --import 55 | curl -Os https://uploader.codecov.io/v0.8.0/aarch64/codecov 56 | curl -Os https://uploader.codecov.io/v0.8.0/aarch64/codecov.SHA256SUM 57 | curl -Os https://uploader.codecov.io/v0.8.0/aarch64/codecov.SHA256SUM.sig 58 | gpgv codecov.SHA256SUM.sig codecov.SHA256SUM 59 | shasum -a 256 -c codecov.SHA256SUM 60 | sudo chmod +x codecov 61 | 62 | jobs: 63 | tests-java: 64 | parameters: 65 | maven-image: 66 | type: string 67 | default: << pipeline.parameters.default-maven-image >> 68 | exe: 69 | type: string 70 | default: docker-amd64-image 71 | arg-line: 72 | type: string 73 | default: "--add-opens=java.base/java.nio=ALL-UNNAMED" 74 | executor: 75 | name: << parameters.exe >> 76 | maven-image: << parameters.maven-image >> 77 | steps: 78 | - checkout 79 | - restore_cache: 80 | name: Restoring Maven Cache 81 | keys: 82 | - &cache-key maven-cache_v1-<< parameters.maven-image >>-{{ checksum "pom.xml" }} 83 | - maven-cache_v3-<< parameters.maven-image >>- 84 | - run: 85 | name: Setup InfluxDB service 86 | command: | 87 | ./scripts/influxdb-setup.sh \ 88 | --export-url-as TESTING_INFLUXDB_URL \ 89 | --export-db-as TESTING_INFLUXDB_DATABASE \ 90 | --export-token-as TESTING_INFLUXDB_TOKEN 91 | - run: 92 | name: "Running tests" 93 | command: | 94 | mvn -B -U clean install -DargLine="@{argLine} << parameters.arg-line >>" 95 | - save_cache: 96 | name: Saving Maven Cache 97 | key: *cache-key 98 | paths: 99 | - ~/.m2 100 | - run: 101 | name: "Copying test results" 102 | when: always 103 | command: | 104 | mkdir test-results 105 | cp target/surefire-reports/*.xml test-results/ || true 106 | - store_test_results: 107 | path: test-results 108 | - run: 109 | name: "Copying artifacts" 110 | command: | 111 | mkdir artifacts 112 | cp -r target/*.jar artifacts/ 113 | - store_artifacts: 114 | path: artifacts 115 | - when: 116 | condition: 117 | equal: [ docker-amd64-image, << parameters.exe >> ] 118 | steps: 119 | - upload-codecov-amd64 120 | - when: 121 | condition: 122 | equal: [ docker-arm64-image, << parameters.exe >> ] 123 | steps: 124 | - upload-codecov-arm64 125 | 126 | check-dependencies: 127 | parameters: 128 | maven-image: 129 | type: string 130 | default: << pipeline.parameters.default-maven-image >> 131 | docker: 132 | - image: << pipeline.parameters.default-maven-image >> 133 | steps: 134 | - checkout 135 | - restore_cache: 136 | name: Restoring Maven Cache 137 | keys: 138 | - maven-cache_v1-<< parameters.maven-image >>- 139 | - run: 140 | name: "Check dependency rules" 141 | command: mvn enforcer:enforce -Denforcer.rules=banDuplicatePomDependencyVersions,dependencyConvergence 142 | 143 | check-licenses: 144 | parameters: 145 | maven-image: 146 | type: string 147 | default: << pipeline.parameters.default-maven-image >> 148 | docker: 149 | - image: << parameters.maven-image >> 150 | steps: 151 | - checkout 152 | - restore_cache: 153 | name: Restoring Maven Cache 154 | keys: 155 | - maven-cache_v1-<< parameters.maven-image >>- 156 | - run: 157 | name: "Check dependency licenses" 158 | command: mvn license:check -Dlicense.dependencies.enforce=true 159 | 160 | check-generate-site: 161 | parameters: 162 | maven-image: 163 | type: string 164 | default: << pipeline.parameters.default-maven-image >> 165 | docker: 166 | - image: << parameters.maven-image >> 167 | steps: 168 | - checkout 169 | - restore_cache: 170 | name: Restoring Maven Cache 171 | keys: 172 | - maven-cache_v1-<< parameters.maven-image >>- 173 | - run: 174 | name: "Check generate site" 175 | command: mvn clean site site:stage -DskipTests 176 | 177 | deploy-snapshot: 178 | docker: 179 | - image: << pipeline.parameters.default-maven-image >> 180 | steps: 181 | - run: 182 | name: Early return if this build is from a forked repository 183 | command: | 184 | if [[ $CIRCLE_PROJECT_USERNAME != "InfluxCommunity" ]]; then 185 | echo "Nothing to do for forked repositories, so marking this step successful" 186 | circleci step halt 187 | fi 188 | - checkout 189 | - run: 190 | name: Early return if this build is not a Snapshot version 191 | command: | 192 | sudo apt-get update 193 | sudo apt-get install libxml2-utils 194 | export PROJECT_VERSION=$(xmllint --xpath "//*[local-name()='project']/*[local-name()='version']/text()" pom.xml) 195 | echo "Project version: $PROJECT_VERSION" 196 | if [[ $PROJECT_VERSION != *SNAPSHOT ]]; then 197 | echo "Nothing to do for this build, so marking this step successful" 198 | circleci step halt 199 | fi 200 | - restore_cache: 201 | name: Restoring Maven Cache 202 | keys: 203 | - &cache-key-deploy maven-cache-deploy_v1-{{ checksum "pom.xml" }} 204 | - maven-cache-deploy_v1- 205 | - run: 206 | name: Deploying Snapshot 207 | command: | 208 | mvn -s ./deploy-settings.xml -DskipTests=true clean deploy 209 | - save_cache: 210 | name: Saving Maven Cache 211 | key: *cache-key-deploy 212 | paths: 213 | - ~/.m2 214 | 215 | workflows: 216 | version: 2 217 | build: 218 | jobs: 219 | - check-dependencies 220 | - check-licenses 221 | - check-generate-site 222 | # Java tests matrix for JDK under 25 223 | - tests-java: 224 | matrix: 225 | alias: tests-java-jdk-old 226 | parameters: 227 | exe: [ docker-amd64-image, docker-arm64-image ] 228 | maven-image: [ << pipeline.parameters.default-maven-image >>, "cimg/openjdk:17.0", "cimg/openjdk:21.0" ] 229 | # Java test matrix for JDK 25+ 230 | - tests-java: 231 | matrix: 232 | alias: tests-java-jdk-25+ 233 | parameters: 234 | exe: [ docker-amd64-image, docker-arm64-image ] 235 | maven-image: [ "cimg/openjdk:25.0" ] 236 | arg-line: [ "--add-opens=java.base/java.nio=ALL-UNNAMED --sun-misc-unsafe-memory-access=allow" ] 237 | - deploy-snapshot: 238 | requires: 239 | - check-dependencies 240 | - check-licenses 241 | - check-generate-site 242 | - tests-java-jdk-old 243 | - tests-java-jdk-25+ 244 | filters: 245 | branches: 246 | only: main 247 | -------------------------------------------------------------------------------- /src/test/java/com/influxdb/v3/client/PointValuesTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client; 23 | 24 | import java.math.BigInteger; 25 | import java.time.Instant; 26 | import java.util.HashMap; 27 | import java.util.Map; 28 | 29 | import org.assertj.core.api.Assertions; 30 | import org.junit.jupiter.api.Test; 31 | 32 | public class PointValuesTest { 33 | @Test 34 | void asPoint() { 35 | PointValues pointValues = new PointValues() 36 | .setTag("tag1", "value1") 37 | .setField("field1", 42); 38 | 39 | Point point = pointValues.asPoint("measurement"); 40 | 41 | Assertions.assertThat(pointValues.getMeasurement()).isEqualTo(point.getMeasurement()); 42 | Assertions.assertThat(pointValues.getTag("tag1")).isEqualTo(point.getTag("tag1")); 43 | Assertions.assertThat(pointValues.getField("field1")).isEqualTo(point.getField("field1")); 44 | } 45 | 46 | @Test 47 | void setMeasurement() { 48 | PointValues pointValues = PointValues.measurement("measurement"); 49 | Assertions.assertThat("measurement").isEqualTo(pointValues.getMeasurement()); 50 | 51 | pointValues.setMeasurement("newMeasurement"); 52 | Assertions.assertThat("newMeasurement").isEqualTo(pointValues.getMeasurement()); 53 | } 54 | 55 | @Test 56 | void setTimestamp() { 57 | PointValues pointValues = PointValues.measurement("measurement"); 58 | 59 | Instant timestamp = Instant.parse("2023-11-08T12:00:00Z"); 60 | pointValues.setTimestamp(timestamp); 61 | Assertions.assertThat(BigInteger.valueOf(timestamp.getEpochSecond()) 62 | .multiply(BigInteger.valueOf(1_000_000_000))) 63 | .isEqualTo(pointValues.getTimestamp()); 64 | } 65 | 66 | @Test 67 | void setTags() { 68 | PointValues pointValues = PointValues.measurement("measurement"); 69 | 70 | Map tags = new HashMap<>(); 71 | tags.put("tag1", "value1"); 72 | tags.put("tag2", "value2"); 73 | 74 | pointValues.setTags(tags); 75 | 76 | Assertions.assertThat("value1").isEqualTo(pointValues.getTag("tag1")); 77 | Assertions.assertThat("value2").isEqualTo(pointValues.getTag("tag2")); 78 | } 79 | 80 | @Test 81 | void removeTag() { 82 | PointValues pointValues = PointValues.measurement("measurement") 83 | .setTag("tag1", "value1") 84 | .setTag("tag2", "value2"); 85 | 86 | pointValues.removeTag("tag1"); 87 | pointValues.removeTag("tagNonExistent"); 88 | 89 | Assertions.assertThat(pointValues.getTag("tag1")).isNull(); 90 | Assertions.assertThat(pointValues.getTag("tag2")).isEqualTo("value2"); 91 | } 92 | 93 | @Test 94 | void getTagNames() { 95 | PointValues pointValues = PointValues.measurement("measurement") 96 | .setTag("tag1", "value1") 97 | .setTag("tag2", "value2"); 98 | 99 | Assertions.assertThat(pointValues.getTagNames()).isEqualTo(new String[]{"tag1", "tag2"}); 100 | } 101 | 102 | @Test 103 | void setGetTypeField() { 104 | PointValues pointValues = PointValues.measurement("measurement"); 105 | 106 | double floatValue = 2.71; 107 | long integerValue = 64L; 108 | boolean booleanValue = true; 109 | String stringValue = "text"; 110 | 111 | pointValues.setFloatField("floatField", floatValue); 112 | pointValues.setIntegerField("integerField", integerValue); 113 | pointValues.setBooleanField("booleanField", booleanValue); 114 | pointValues.setStringField("stringField", stringValue); 115 | 116 | Assertions.assertThat(pointValues.getFloatField("floatField")).isEqualTo(floatValue); 117 | Assertions.assertThat(pointValues.getIntegerField("integerField")).isEqualTo(integerValue); 118 | Assertions.assertThat(pointValues.getBooleanField("booleanField")).isEqualTo(booleanValue); 119 | Assertions.assertThat(pointValues.getStringField("stringField")).isEqualTo(stringValue); 120 | } 121 | 122 | @Test 123 | void fieldGenerics() { 124 | PointValues pointValues = PointValues.measurement("measurement"); 125 | 126 | double floatValue = 2.71; 127 | long integerValue = 64L; 128 | boolean booleanValue = true; 129 | String stringValue = "text"; 130 | 131 | pointValues.setField("floatField", floatValue); 132 | pointValues.setField("integerField", integerValue); 133 | pointValues.setField("booleanField", booleanValue); 134 | pointValues.setField("stringField", stringValue); 135 | 136 | Assertions.assertThat(pointValues.getField("floatField", Double.class)).isEqualTo(floatValue); 137 | Assertions.assertThat(pointValues.getFieldType("floatField")).isEqualTo(Double.class); 138 | Assertions.assertThat(pointValues.getField("integerField", Long.class)).isEqualTo(integerValue); 139 | Assertions.assertThat(pointValues.getFieldType("integerField")).isEqualTo(Long.class); 140 | Assertions.assertThat(pointValues.getField("booleanField", Boolean.class)).isEqualTo(booleanValue); 141 | Assertions.assertThat(pointValues.getFieldType("booleanField")).isEqualTo(Boolean.class); 142 | Assertions.assertThat(pointValues.getField("stringField", String.class)).isEqualTo(stringValue); 143 | Assertions.assertThat(pointValues.getFieldType("stringField")).isEqualTo(String.class); 144 | Assertions.assertThat(pointValues.getField("Missing", String.class)).isNull(); 145 | Assertions.assertThat(pointValues.getFieldType("Missing")).isNull(); 146 | } 147 | 148 | @Test 149 | void setFields() { 150 | PointValues pointValues = PointValues.measurement("measurement"); 151 | 152 | pointValues.setField("field1", 42); 153 | pointValues.setField("field2", "value"); 154 | pointValues.setField("field3", 3.14); 155 | 156 | Assertions.assertThat(42L).isEqualTo(pointValues.getField("field1")); 157 | Assertions.assertThat("value").isEqualTo(pointValues.getField("field2")); 158 | Assertions.assertThat(3.14).isEqualTo(pointValues.getField("field3")); 159 | } 160 | 161 | @Test 162 | void removeField() { 163 | PointValues pointValues = PointValues.measurement("measurement") 164 | .setField("field1", 42) 165 | .setField("field2", "value") 166 | .setField("field3", 3.14); 167 | 168 | pointValues.removeField("field1") 169 | .removeField("field2"); 170 | 171 | Assertions.assertThat(pointValues.getField("field1")).isNull(); 172 | Assertions.assertThat(pointValues.getField("field2")).isNull(); 173 | Assertions.assertThat(3.14).isEqualTo(pointValues.getField("field3")); 174 | } 175 | 176 | @Test 177 | void getFieldNames() { 178 | PointValues pointValues = PointValues.measurement("measurement") 179 | .setField("field", 42) 180 | .setField("123", "value") 181 | .setField("some_name", 3.14); 182 | 183 | Assertions.assertThat(pointValues.getFieldNames()) 184 | .isEqualTo(new String[]{"123", "field", "some_name"}); 185 | } 186 | 187 | @Test 188 | void copy() { 189 | PointValues pointValues = PointValues.measurement("measurement") 190 | .setTag("tag1", "value1") 191 | .setField("field1", 42); 192 | 193 | PointValues copy = pointValues.copy(); 194 | 195 | // Ensure the copy is not the same object 196 | Assertions.assertThat(pointValues).isNotSameAs(copy); 197 | // Ensure the values are equal 198 | Assertions.assertThat(pointValues.getMeasurement()).isEqualTo(copy.getMeasurement()); 199 | Assertions.assertThat(pointValues.getTag("tag1")).isEqualTo(copy.getTag("tag1")); 200 | Assertions.assertThat(pointValues.getField("field1")).isEqualTo(copy.getField("field1")); 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /src/test/java/com/influxdb/v3/client/PointTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client; 23 | 24 | import java.math.BigInteger; 25 | import java.time.Instant; 26 | import java.util.HashMap; 27 | import java.util.Map; 28 | 29 | import org.assertj.core.api.Assertions; 30 | import org.junit.jupiter.api.Test; 31 | 32 | import com.influxdb.v3.client.write.WritePrecision; 33 | 34 | public class PointTest { 35 | @Test 36 | void fromValues() throws Exception { 37 | PointValues pointValues = PointValues.measurement("measurement") 38 | .setField("field1", 42); 39 | Point point = Point.fromValues(pointValues); 40 | 41 | Assertions.assertThat("measurement").isEqualTo(point.getMeasurement()); 42 | 43 | Assertions.assertThat(42L).isEqualTo(point.getField("field1")); 44 | point.setMeasurement("newMeasurement"); 45 | Assertions.assertThat("newMeasurement").isEqualTo(point.getMeasurement()); 46 | Assertions.assertThat("newMeasurement").isEqualTo(pointValues.getMeasurement()); 47 | } 48 | 49 | @Test 50 | void setMeasurement() { 51 | Point point = Point.measurement("measurement"); 52 | Assertions.assertThat("measurement").isEqualTo(point.getMeasurement()); 53 | 54 | point.setMeasurement("newMeasurement"); 55 | Assertions.assertThat("newMeasurement").isEqualTo(point.getMeasurement()); 56 | } 57 | 58 | @Test 59 | void setTimestamp() { 60 | Point point = Point.measurement("measurement"); 61 | 62 | Instant timestamp = Instant.parse("2023-11-08T12:00:00Z"); 63 | point.setTimestamp(timestamp); 64 | Assertions.assertThat(BigInteger.valueOf(timestamp.getEpochSecond()) 65 | .multiply(BigInteger.valueOf(1_000_000_000))) 66 | .isEqualTo(point.getTimestamp()); 67 | } 68 | 69 | @Test 70 | void setTags() { 71 | Point point = Point.measurement("measurement"); 72 | 73 | Map tags = new HashMap<>(); 74 | tags.put("tag1", "value1"); 75 | tags.put("tag2", "value2"); 76 | 77 | point.setTags(tags); 78 | 79 | Assertions.assertThat(point.getTag("tag1")).isEqualTo("value1"); 80 | Assertions.assertThat(point.getTag("tag2")).isEqualTo("value2"); 81 | } 82 | 83 | @Test 84 | void removeTag() { 85 | Point point = Point.measurement("measurement") 86 | .setTag("tag1", "value1") 87 | .setTag("tag2", "value2"); 88 | 89 | point.removeTag("tag1"); 90 | point.removeTag("tagNonExistent"); 91 | 92 | Assertions.assertThat(point.getTag("tag1")).isNull(); 93 | Assertions.assertThat(point.getTag("tag2")).isEqualTo("value2"); 94 | } 95 | 96 | @Test 97 | void getTagNames() { 98 | Point point = Point.measurement("measurement") 99 | .setTag("tag1", "value1") 100 | .setTag("tag2", "value2"); 101 | 102 | Assertions.assertThat(point.getTagNames()).isEqualTo(new String[]{"tag1", "tag2"}); 103 | } 104 | 105 | @Test 106 | void setGetTypeField() { 107 | Point point = Point.measurement("measurement"); 108 | 109 | double floatValue = 2.71; 110 | long integerValue = 64L; 111 | boolean booleanValue = true; 112 | String stringValue = "text"; 113 | 114 | point.setFloatField("floatField", floatValue); 115 | point.setIntegerField("integerField", integerValue); 116 | point.setBooleanField("booleanField", booleanValue); 117 | point.setStringField("stringField", stringValue); 118 | 119 | Assertions.assertThat(point.getFloatField("floatField")).isEqualTo(floatValue); 120 | Assertions.assertThat(point.getIntegerField("integerField")).isEqualTo(integerValue); 121 | Assertions.assertThat(point.getBooleanField("booleanField")).isEqualTo(booleanValue); 122 | Assertions.assertThat(point.getStringField("stringField")).isEqualTo(stringValue); 123 | } 124 | 125 | @Test 126 | void fieldGenerics() { 127 | Point point = Point.measurement("measurement"); 128 | 129 | double floatValue = 2.71; 130 | long integerValue = 64L; 131 | boolean booleanValue = true; 132 | String stringValue = "text"; 133 | 134 | point.setField("floatField", floatValue); 135 | point.setField("integerField", integerValue); 136 | point.setField("booleanField", booleanValue); 137 | point.setField("stringField", stringValue); 138 | 139 | Assertions.assertThat(point.getField("floatField", Double.class)).isEqualTo(floatValue); 140 | Assertions.assertThat(point.getFieldType("floatField")).isEqualTo(Double.class); 141 | Assertions.assertThat(point.getField("integerField", Long.class)).isEqualTo(integerValue); 142 | Assertions.assertThat(point.getFieldType("integerField")).isEqualTo(Long.class); 143 | Assertions.assertThat(point.getField("booleanField", Boolean.class)).isEqualTo(booleanValue); 144 | Assertions.assertThat(point.getFieldType("booleanField")).isEqualTo(Boolean.class); 145 | Assertions.assertThat(point.getField("stringField", String.class)).isEqualTo(stringValue); 146 | Assertions.assertThat(point.getFieldType("stringField")).isEqualTo(String.class); 147 | Assertions.assertThat(point.getField("Missing", String.class)).isNull(); 148 | Assertions.assertThat(point.getFieldType("Missing")).isNull(); 149 | } 150 | 151 | @Test 152 | void setFields() { 153 | Point point = Point.measurement("measurement"); 154 | 155 | point.setField("field1", 42); 156 | point.setField("field2", "value"); 157 | point.setField("field3", 3.14); 158 | 159 | Assertions.assertThat(42L).isEqualTo(point.getField("field1")); 160 | Assertions.assertThat("value").isEqualTo(point.getField("field2")); 161 | Assertions.assertThat(3.14).isEqualTo(point.getField("field3")); 162 | } 163 | 164 | @Test 165 | void removeField() { 166 | Point point = Point.measurement("measurement") 167 | .setField("field1", 42) 168 | .setField("field2", "value") 169 | .setField("field3", 3.14); 170 | 171 | point.removeField("field1") 172 | .removeField("field2"); 173 | 174 | Assertions.assertThat(point.getField("field1")).isNull(); 175 | Assertions.assertThat(point.getField("field2")).isNull(); 176 | Assertions.assertThat(3.14).isEqualTo(point.getField("field3")); 177 | } 178 | 179 | @Test 180 | void getFieldNames() { 181 | Point point = Point.measurement("measurement") 182 | .setField("field", 42) 183 | .setField("123", "value") 184 | .setField("some_name", 3.14); 185 | 186 | Assertions.assertThat(point.getFieldNames()) 187 | .isEqualTo(new String[]{"123", "field", "some_name"}); 188 | } 189 | 190 | @Test 191 | void toLineProtocol() { 192 | Point point = Point.measurement("measurement") 193 | .setTag("tag1", "value1") 194 | .setField("field1", 42); 195 | 196 | String lineProtocol = point.toLineProtocol(WritePrecision.NS); 197 | Assertions.assertThat("measurement,tag1=value1 field1=42i").isEqualTo(lineProtocol); 198 | } 199 | 200 | @Test 201 | void copy() { 202 | Point point = Point.measurement("measurement") 203 | .setTag("tag1", "value1") 204 | .setField("field1", 42); 205 | 206 | Point copy = point.copy(); 207 | 208 | // Ensure the copy is not the same object 209 | Assertions.assertThat(point).isNotSameAs(copy); 210 | // Ensure the values are equal 211 | Assertions.assertThat(point.getMeasurement()).isEqualTo(copy.getMeasurement()); 212 | Assertions.assertThat(point.getTag("tag1")).isEqualTo(copy.getTag("tag1")); 213 | Assertions.assertThat(point.getField("field1")).isEqualTo(copy.getField("field1")); 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /src/main/java/com/influxdb/v3/client/internal/VectorSchemaRootConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package com.influxdb.v3.client.internal; 23 | 24 | import java.math.BigInteger; 25 | import java.time.LocalDateTime; 26 | import java.util.LinkedHashMap; 27 | import java.util.List; 28 | import java.util.Map; 29 | import java.util.Objects; 30 | import java.util.logging.Logger; 31 | import javax.annotation.Nonnull; 32 | import javax.annotation.Nullable; 33 | import javax.annotation.concurrent.ThreadSafe; 34 | 35 | import org.apache.arrow.vector.FieldVector; 36 | import org.apache.arrow.vector.VectorSchemaRoot; 37 | import org.apache.arrow.vector.types.pojo.Field; 38 | import org.apache.arrow.vector.util.Text; 39 | 40 | import com.influxdb.v3.client.PointValues; 41 | import com.influxdb.v3.client.write.WritePrecision; 42 | 43 | 44 | /** 45 | * The VectorSchemaRootConverter class is responsible for converting rows of data from a VectorSchemaRoot object 46 | * to PointValues. It provides a method to perform this conversion. 47 | *

48 | * This class is thread-safe. 49 | */ 50 | @ThreadSafe 51 | public final class VectorSchemaRootConverter { 52 | 53 | private static final Logger LOG = Logger.getLogger(VectorSchemaRootConverter.class.getName()); 54 | 55 | public static final VectorSchemaRootConverter INSTANCE = new VectorSchemaRootConverter(); 56 | 57 | /** 58 | * Converts a given row of data from a VectorSchemaRoot object to PointValues. 59 | * 60 | * @param rowNumber the index of the row to be converted 61 | * @param fieldVectors the list of FieldVector objects representing the data columns 62 | * @return the converted PointValues object 63 | */ 64 | @Nonnull 65 | PointValues toPointValues(final int rowNumber, 66 | @Nonnull final List fieldVectors) { 67 | PointValues p = new PointValues(); 68 | for (FieldVector fieldVector : fieldVectors) { 69 | var field = fieldVector.getField(); 70 | var value = fieldVector.getObject(rowNumber); 71 | var fieldName = field.getName(); 72 | var metaType = field.getMetadata().get("iox::column::type"); 73 | 74 | if (value instanceof Text) { 75 | value = value.toString(); 76 | } 77 | 78 | if ((Objects.equals(fieldName, "measurement") 79 | || Objects.equals(fieldName, "iox::measurement")) 80 | && value instanceof String) { 81 | p.setMeasurement((String) value); 82 | continue; 83 | } 84 | 85 | if (metaType == null) { 86 | if (Objects.equals(fieldName, "time") && (value instanceof Long || value instanceof LocalDateTime)) { 87 | var timeNano = NanosecondConverter.getTimestampNano(value, field); 88 | p.setTimestamp(timeNano, WritePrecision.NS); 89 | } else { 90 | // just push as field If you don't know what type is it 91 | p.setField(fieldName, value); 92 | } 93 | 94 | continue; 95 | } 96 | 97 | String valueType = metaType.split("::")[2]; 98 | Object mappedValue = getMappedValue(field, value); 99 | if ("field".equals(valueType)) { 100 | p.setField(fieldName, mappedValue); 101 | } else if ("tag".equals(valueType) && value instanceof String) { 102 | p.setTag(fieldName, (String) mappedValue); 103 | } else if ("timestamp".equals(valueType)) { 104 | p.setTimestamp((BigInteger) mappedValue, WritePrecision.NS); 105 | } 106 | } 107 | return p; 108 | } 109 | 110 | /** 111 | * Function to cast value return base on metadata from InfluxDB. 112 | * 113 | * @param field the Field object from Arrow 114 | * @param value the value to cast 115 | * @return the value with the correct type 116 | */ 117 | public Object getMappedValue(@Nonnull final Field field, @Nullable final Object value) { 118 | if (value == null) { 119 | return null; 120 | } 121 | 122 | var fieldName = field.getName(); 123 | if ("measurement".equals(fieldName) || "iox::measurement".equals(fieldName)) { 124 | return value.toString(); 125 | } 126 | 127 | var metaType = field.getMetadata().get("iox::column::type"); 128 | if (metaType == null) { 129 | if ("time".equals(fieldName) && (value instanceof Long || value instanceof LocalDateTime)) { 130 | return NanosecondConverter.getTimestampNano(value, field); 131 | } else { 132 | return value; 133 | } 134 | } 135 | 136 | String[] parts = metaType.split("::"); 137 | String valueType = parts[2]; 138 | if ("field".equals(valueType)) { 139 | switch (metaType) { 140 | case "iox::column_type::field::integer": 141 | case "iox::column_type::field::uinteger": 142 | if (value instanceof Number) { 143 | return TypeCasting.toLongValue(value); 144 | } else { 145 | LOG.warning(String.format("Value %s is not an Long", value)); 146 | return value; 147 | } 148 | case "iox::column_type::field::float": 149 | if (value instanceof Number) { 150 | return TypeCasting.toDoubleValue(value); 151 | } else { 152 | LOG.warning(String.format("Value %s is not a Double", value)); 153 | return value; 154 | } 155 | case "iox::column_type::field::string": 156 | if (value instanceof Text || value instanceof String) { 157 | return TypeCasting.toStringValue(value); 158 | } else { 159 | LOG.warning(String.format("Value %s is not a String", value)); 160 | return value; 161 | } 162 | case "iox::column_type::field::boolean": 163 | if (value instanceof Boolean) { 164 | return value; 165 | } else { 166 | LOG.warning(String.format("Value %s is not a Boolean", value)); 167 | return value; 168 | } 169 | default: 170 | return value; 171 | } 172 | } else if ("timestamp".equals(valueType) || Objects.equals(fieldName, "time")) { 173 | return NanosecondConverter.getTimestampNano(value, field); 174 | } else { 175 | return TypeCasting.toStringValue(value); 176 | } 177 | } 178 | 179 | /** 180 | * Get array of values from VectorSchemaRoot. 181 | * 182 | * @param vector The data return from InfluxDB. 183 | * @param rowNumber The row number of data 184 | * @return An array of Objects represents a row of data 185 | */ 186 | public Object[] getArrayObjectFromVectorSchemaRoot(@Nonnull final VectorSchemaRoot vector, final int rowNumber) { 187 | List fieldVectors = vector.getFieldVectors(); 188 | int columnSize = fieldVectors.size(); 189 | var row = new Object[columnSize]; 190 | for (int i = 0; i < columnSize; i++) { 191 | FieldVector fieldVector = fieldVectors.get(i); 192 | row[i] = getMappedValue( 193 | fieldVector.getField(), 194 | fieldVector.getObject(rowNumber) 195 | ); 196 | } 197 | 198 | return row; 199 | } 200 | 201 | /** 202 | * Get a Map from VectorSchemaRoot. 203 | * 204 | * @param vector The data return from InfluxDB. 205 | * @param rowNumber The row number of data 206 | * @return A Map represents a row of data 207 | */ 208 | public Map getMapFromVectorSchemaRoot(@Nonnull final VectorSchemaRoot vector, final int rowNumber) { 209 | Map row = new LinkedHashMap<>(); 210 | for (FieldVector fieldVector : vector.getFieldVectors()) { 211 | Object mappedValue = getMappedValue( 212 | fieldVector.getField(), 213 | fieldVector.getObject(rowNumber) 214 | ); 215 | row.put(fieldVector.getName(), mappedValue); 216 | 217 | } 218 | 219 | return row; 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.8.0 [unreleased] 2 | 3 | ### CI 4 | 5 | 1. [#313](https://github.com/InfluxCommunity/influxdb3-java/pull/313): Clarify JDK 25+ requirements. 6 | 7 | ## 1.7.0 [2025-11-21] 8 | 9 | ### Bug Fixes 10 | 11 | 1. [#317](https://github.com/InfluxCommunity/influxdb3-java/pull/317): Fix Arrow memory leak when stream close fails due to thread interrupts. 12 | 1. [#318](https://github.com/InfluxCommunity/influxdb3-java/pull/318): Explicit releasing of the VectorSchemaRoot. 13 | 14 | ## 1.6.0 [2025-11-14] 15 | 16 | ### Features 17 | 18 | 1. [#306](https://github.com/InfluxCommunity/influxdb3-java/pull/306): Improve closing of Arrow `FlightStream`. 19 | 20 | ### Bug Fixes 21 | 22 | 1. [#310](https://github.com/InfluxCommunity/influxdb3-java/pull/310): Ensure `QueryOptions` objects are left unchanged within the `queryData` implementation. 23 | 24 | ## 1.5.0 [2025-10-22] 25 | 26 | ### Features 27 | 28 | 1. [#289](https://github.com/InfluxCommunity/influxdb3-java/pull/289) Add the possibility to disable gRPC compression via the `disableGRPCCompression` parameter in the `ClientConfig`. 29 | 30 | ### CI 31 | 32 | 1. [#283](https://github.com/InfluxCommunity/influxdb3-java/pull/283) Fix pipeline not downloading the correct java images. 33 | 34 | ## 1.4.0 [2025-09-15] 35 | 36 | ### Features 37 | 38 | 1. [#265](https://github.com/InfluxCommunity/influxdb3-java/pull/265) Add more precise timeout properties to `ClientConfig`. 39 | 1. Current property `timeout` is deprecated, as it applies only to the Write API and can be confusing to some users. 40 | 2. Two new properties are added, along with getters and similar setters in the `ClientConfig.Builder`. 41 | 1. `writeTimeout` - a `java.time.Duration` that applies only to the Write API. 42 | 2. `queryTimeout` - a `java.time.Duration` used to calculate deadlines when using the Query API. 43 | 3. These properties can also be defined when creating a client using environment variables. Respectively: 44 | 1. `INFLUX_WRITE_TIMEOUT` - a positive integer. The time unit is in seconds. 45 | 2. `INFLUX_QUERY_TIMEOUT` - a positive integer. The time unit is in seconds. 46 | 4. These properties can also be defined when creating a client using system properties. Respectively: 47 | 1. `influx.writeTimeout` - a positive integer. The time unit is in seconds. 48 | 2. `influx.queryTimeout` - a positive integer. The time unit is in seconds. 49 | 50 | ### CI 51 | 52 | 1. [#266](https://github.com/InfluxCommunity/influxdb3-java/pull/266) Add tests for arm64 CircleCI. 53 | 54 | ## 1.3.0 [2025-08-13] 55 | 56 | ### Features 57 | 58 | 1. [#250](https://github.com/InfluxCommunity/influxdb3-java/pull/250) Upgrade Netty version to 4.2.3.Final. 59 | 2. [#251](https://github.com/InfluxCommunity/influxdb3-java/pull/251) Add comment warning null when calling getMeasurement function. 60 | 3. [#252](https://github.com/InfluxCommunity/influxdb3-java/pull/252) Run integration tests against a locally started InfluxDB 3 Core server. 61 | 62 | ### Documentation 63 | 64 | 1. [#253](https://github.com/InfluxCommunity/influxdb3-java/pull/253) New Durable example showing client reuse for better resource management. 65 | 66 | ## 1.2.0 [2025-06-26] 67 | 68 | ### Features 69 | 70 | 1. [#209](https://github.com/InfluxCommunity/influxdb3-java/pull/209) Add query function returning row as map. 71 | 2. [#238](https://github.com/InfluxCommunity/influxdb3-java/pull/238): Support fast writes without waiting for WAL 72 | persistence: 73 | - New write option (`WriteOptions.noSync`) added: `true` value means faster write but without the confirmation that 74 | the data was persisted. Default value: `false`. 75 | - **Supported by self-managed InfluxDB 3 Core and Enterprise servers only!** 76 | - Also configurable via connection string query parameter (`writeNoSync`). 77 | - Also configurable via environment variable (`INFLUX_WRITE_NO_SYNC`). 78 | - Long precision string values added from v3 HTTP API: `"nanosecond"`, `"microsecond"`, `"millisecond"`, 79 | `"second"` ( 80 | in addition to the existing `"ns"`, `"us"`, `"ms"`, `"s"`). 81 | 3. [#241](https://github.com/InfluxCommunity/influxdb3-java/pull/241): Some default options will be used from a getter. 82 | 4. [#243](https://github.com/InfluxCommunity/influxdb3-java/pull/243): Add function to get InfluxDB version. 83 | 84 | ### Bug Fixes 85 | 86 | 1. [#239](https://github.com/InfluxCommunity/influxdb3-java/pull/239): Use write options from `ClientConfig` in 87 | `InfluxDBClientImpl` write methods: 88 | 89 | ```java 90 | public void writeRecord(@Nullable final String record); 91 | public void writeRecords(@Nonnull final List records); 92 | public void writePoint(@Nullable final Point point); 93 | public void writePoints(@Nonnull final List points); 94 | ``` 95 | 96 | ## 1.1.0 [2025-05-22] 97 | 98 | ### Features 99 | 100 | 1. [#229](https://github.com/InfluxCommunity/influxdb3-java/pull/229): Support proxy and custom ssl root certificates 101 | 2. [#232](https://github.com/InfluxCommunity/influxdb3-java/pull/232): Allow set rpc max message size through maxInboundMessageSize in ClientConfig 102 | 3. [#233](https://github.com/InfluxCommunity/influxdb3-java/pull/233): More detailed documentation about timestamp handling for query and write functions 103 | 4. [#236](https://github.com/InfluxCommunity/influxdb3-java/pull/236): Supports Java 21. 104 | 105 | ## 1.0.0 [2024-12-11] 106 | 107 | ### Features 108 | 109 | 1. [#200](https://github.com/InfluxCommunity/influxdb3-java/pull/200): Respect iox::column_type::field metadata when 110 | mapping query results into values. 111 | - iox::column_type::field::integer: => Long 112 | - iox::column_type::field::uinteger: => Long 113 | - iox::column_type::field::float: => Double 114 | - iox::column_type::field::string: => String 115 | - iox::column_type::field::boolean: => Boolean 116 | 117 | ### Dependencies 118 | 119 | 1. [#202](https://github.com/InfluxCommunity/influxdb3-java/pull/202): Migrate from `flight-grpc` to `flight-core` package. 120 | 121 | ## 0.9.0 [2024-08-12] 122 | 123 | ### Features 124 | 125 | 1. [#158](https://github.com/InfluxCommunity/influxdb3-java/pull/158): Add InfluxDB Edge (OSS) authentication support. 126 | 1. [#163](https://github.com/InfluxCommunity/influxdb3-java/pull/163): Introduces `InfluxDBApiHttpException` to facilitate write retries and error recovery. 127 | 128 | ### Bug Fixes 129 | 130 | 1. [#148](https://github.com/InfluxCommunity/influxdb3-java/pull/148): InfluxDB Edge (OSS) error handling 131 | 1. [#153](https://github.com/InfluxCommunity/influxdb3-java/pull/153): Parsing timestamp columns 132 | 133 | ## 0.8.0 [2024-06-24] 134 | 135 | ### Features 136 | 137 | 1. [#144](https://github.com/InfluxCommunity/influxdb3-java/pull/133): user-agent header is updated for both REST and gRPC calls. 138 | 139 | ## 0.7.0 [2024-03-11] 140 | 141 | ### Features 142 | 143 | 1. [#107](https://github.com/InfluxCommunity/influxdb3-java/pull/107): Custom headers are also supported for the query (gRPC request) 144 | 145 | ```java 146 | ClientConfig config = new ClientConfig.Builder() 147 | .host("https://us-east-1-1.aws.cloud2.influxdata.com") 148 | .token("my-token".toCharArray()) 149 | .database("my-database") 150 | .headers(Map.of("X-Tracing-Id", "123")) 151 | .build(); 152 | 153 | try (InfluxDBClient client = InfluxDBClient.getInstance(config)) { 154 | // 155 | // your code here 156 | // 157 | } catch (Exception e) { 158 | throw new RuntimeException(e); 159 | } 160 | ``` 161 | 162 | 1. [#108](https://github.com/InfluxCommunity/influxdb3-java/pull/108): Custom headers can be specified per request (query/write): 163 | 164 | ```java 165 | ClientConfig config = new ClientConfig.Builder() 166 | .host("https://us-east-1-1.aws.cloud2.influxdata.com") 167 | .token("my-token".toCharArray()) 168 | .database("my-database") 169 | .build(); 170 | 171 | try (InfluxDBClient client = InfluxDBClient.getInstance(config)) { 172 | // 173 | // Write with custom headers 174 | // 175 | WriteOptions writeOptions = new WriteOptions( 176 | Map.of("X-Tracing-Id", "852") 177 | ); 178 | client.writeRecord("mem,tag=one value=1.0", writeOptions); 179 | 180 | // 181 | // Query with custom headers 182 | // 183 | QueryOptions queryOptions = new QueryOptions( 184 | Map.of("X-Tracing-Id", "852") 185 | ); 186 | Stream rows = client.query("select * from cpu", queryOptions); 187 | 188 | } catch (Exception e) { 189 | throw new RuntimeException(e); 190 | } 191 | ``` 192 | 193 | ## 0.6.0 [2024-03-01] 194 | 195 | ### Features 196 | 197 | 1. [#94](https://github.com/InfluxCommunity/influxdb3-java/pull/94): Add support for named query parameters 198 | 199 | ## 0.5.1 [2024-02-01] 200 | 201 | Resync artifacts with Maven Central. 202 | 203 | ## 0.5.0 [2024-01-30] 204 | 205 | ### Features 206 | 207 | 1. [#78](https://github.com/InfluxCommunity/influxdb3-java/pull/78): Default Tags can be used when writing points. 208 | 209 | ### Bug Fixes 210 | 211 | 1. [#77](https://github.com/InfluxCommunity/influxdb3-java/pull/77): Serialize InfluxDB response to `PointValues` 212 | 213 | ## 0.4.0 [2023-11-08] 214 | 215 | ### Features 216 | 217 | 1. [#41](https://github.com/InfluxCommunity/influxdb3-java/pull/41): Add structured query support 218 | 219 | ## 0.3.1 [2023-10-17] 220 | 221 | ### Bug Fixes 222 | 223 | 1. [#55](https://github.com/InfluxCommunity/influxdb3-java/pull/55): Iteration over more Arrow streams 224 | 225 | ## 0.3.0 [2023-10-02] 226 | 227 | ### Features 228 | 229 | 1. [#40](https://github.com/InfluxCommunity/influxdb3-java/pull/40): Add client creation from connection string, 230 | environment variables or system properties. 231 | 232 | ## 0.2.0 [2023-08-11] 233 | 234 | ### Features 235 | 236 | 1. [#27](https://github.com/InfluxCommunity/influxdb3-java/pull/27): Add GZIP support 237 | 1. [#30](https://github.com/InfluxCommunity/influxdb3-java/pull/30): Add HTTP proxy and custom headers support 238 | 239 | ### Breaking Changes 240 | 241 | 1. [#31](https://github.com/InfluxCommunity/influxdb3-java/pull/31): Renamed config types and some options 242 | 243 | ## 0.1.0 [2023-06-08] 244 | 245 | - initial release of new client version 246 | --------------------------------------------------------------------------------