From e0b87852d519cf31cee961e9c07f7af7ace9dd4c Mon Sep 17 00:00:00 2001 From: Vitaliy Potyarkin Date: Thu, 1 Jan 1970 00:00:00 +0000 Subject: [PATCH 01/38] Empty default branch for forked repos Signed-off-by: Vitaliy Potyarkin --- README.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..7463f9e --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# WIP area: this repo is just a fork! + +Useful things may be published only in [other branches](../../../branches) From 04bf8249c29632f8b00533a9413cc5846c71a671 Mon Sep 17 00:00:00 2001 From: Alex Vanin Date: Thu, 23 May 2024 10:51:32 +0300 Subject: [PATCH 02/38] Initial commit --- .forgejo/workflows/dco.yml | 21 +++++++++++ LICENSE | 74 ++++++++++++++++++++++++++++++++++++++ README.md | 3 ++ 3 files changed, 98 insertions(+) create mode 100644 .forgejo/workflows/dco.yml create mode 100644 LICENSE create mode 100644 README.md diff --git a/.forgejo/workflows/dco.yml b/.forgejo/workflows/dco.yml new file mode 100644 index 0000000..c8aa5e3 --- /dev/null +++ b/.forgejo/workflows/dco.yml @@ -0,0 +1,21 @@ +name: DCO +on: [pull_request] + +jobs: + dco: + name: DCO + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Setup Go + uses: actions/setup-go@v3 + with: + go-version: '1.21' + + - name: Run commit format checker + uses: https://git.frostfs.info/TrueCloudLab/dco-go@v3 + with: + from: 'origin/${{ github.event.pull_request.base.ref }}' diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7e04975 --- /dev/null +++ b/LICENSE @@ -0,0 +1,74 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright 2024 TrueCloudLab + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..9f37b2a --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# frostfs-sdk-java + +Java implementation of FrostFS SDK From 2481774545de18a774f2d88d8a20cbd799f0d5e2 Mon Sep 17 00:00:00 2001 From: Bruk Ori Date: Tue, 11 Jun 2024 17:34:39 +0300 Subject: [PATCH 03/38] [#1] Define SDK structure, add operations with container and object Signed-off-by: Ori Bruk --- .gitignore | 40 + CONTRIBUTING.md | 156 ++++ README.md | 91 ++ client/pom.xml | 48 ++ .../main/java/info/FrostFS/sdk/Client.java | 36 + .../java/info/FrostFS/sdk/GrpcClient.java | 89 ++ .../info/FrostFS/sdk/RequestConstructor.java | 50 ++ .../java/info/FrostFS/sdk/RequestSigner.java | 114 +++ .../main/java/info/FrostFS/sdk/Verifier.java | 116 +++ .../FrostFS/sdk/services/ContainerClient.java | 16 + .../FrostFS/sdk/services/ObjectClient.java | 19 + .../sdk/services/impl/ContainerService.java | 107 +++ .../sdk/services/impl/FrostFSClient.java | 85 ++ .../sdk/services/impl/NetmapService.java | 42 + .../sdk/services/impl/ObjectReader.java | 48 ++ .../sdk/services/impl/ObjectService.java | 257 ++++++ .../sdk/services/impl/ObjectWriter.java | 59 ++ .../sdk/services/impl/SearchReader.java | 26 + .../sdk/services/impl/SessionService.java | 55 ++ cryptography/pom.xml | 44 + .../java/info/FrostFS/sdk/ArrayHelper.java | 12 + .../main/java/info/FrostFS/sdk/Base58.java | 127 +++ .../main/java/info/FrostFS/sdk/Helper.java | 36 + .../java/info/FrostFS/sdk/KeyExtension.java | 161 ++++ modelsV2/pom.xml | 38 + .../java/info/FrostFS/sdk/UUIDExtension.java | 21 + .../FrostFS/sdk/constants/XHeaderConst.java | 7 + .../java/info/FrostFS/sdk/enums/BasicAcl.java | 33 + .../info/FrostFS/sdk/enums/NodeState.java | 33 + .../FrostFS/sdk/enums/ObjectMatchType.java | 16 + .../info/FrostFS/sdk/enums/ObjectType.java | 32 + .../info/FrostFS/sdk/enums/StatusCode.java | 46 + .../java/info/FrostFS/sdk/jdo/Constants.java | 6 + .../java/info/FrostFS/sdk/jdo/Container.java | 58 ++ .../info/FrostFS/sdk/jdo/ContainerId.java | 35 + .../java/info/FrostFS/sdk/jdo/MetaHeader.java | 41 + .../info/FrostFS/sdk/jdo/ObjectAttribute.java | 27 + .../info/FrostFS/sdk/jdo/ObjectFilter.java | 58 ++ .../info/FrostFS/sdk/jdo/ObjectFrostFs.java | 37 + .../info/FrostFS/sdk/jdo/ObjectHeader.java | 71 ++ .../java/info/FrostFS/sdk/jdo/ObjectId.java | 31 + .../java/info/FrostFS/sdk/jdo/OwnerId.java | 25 + .../java/info/FrostFS/sdk/jdo/Status.java | 45 + .../java/info/FrostFS/sdk/jdo/Version.java | 36 + .../info/FrostFS/sdk/jdo/netmap/NodeInfo.java | 30 + .../sdk/jdo/netmap/PlacementPolicy.java | 27 + .../info/FrostFS/sdk/jdo/netmap/Replica.java | 34 + .../sdk/mappers/ContainerIdMapper.java | 16 + .../FrostFS/sdk/mappers/ContainerMapper.java | 39 + .../FrostFS/sdk/mappers/MetaHeaderMapper.java | 15 + .../sdk/mappers/ObjectAttributeMapper.java | 17 + .../sdk/mappers/ObjectFilterMapper.java | 25 + .../sdk/mappers/ObjectHeaderMapper.java | 48 ++ .../FrostFS/sdk/mappers/ObjectIdMapper.java | 13 + .../FrostFS/sdk/mappers/ObjectMapper.java | 15 + .../FrostFS/sdk/mappers/OwnerIdMapper.java | 13 + .../FrostFS/sdk/mappers/StatusMapper.java | 23 + .../FrostFS/sdk/mappers/VersionMapper.java | 18 + .../sdk/mappers/netmap/NodeInfoMapper.java | 21 + .../mappers/netmap/PlacementPolicyMapper.java | 25 + .../sdk/mappers/netmap/ReplicaMapper.java | 17 + pom.xml | 23 + protosV2/pom.xml | 106 +++ .../src/main/proto/accounting/service.proto | 71 ++ .../src/main/proto/accounting/types.proto | 22 + protosV2/src/main/proto/acl/types.proto | 227 +++++ .../src/main/proto/apemanager/service.proto | 172 ++++ .../src/main/proto/apemanager/types.proto | 34 + .../src/main/proto/container/service.proto | 431 +++++++++ protosV2/src/main/proto/container/types.proto | 76 ++ protosV2/src/main/proto/lock/types.proto | 19 + protosV2/src/main/proto/netmap/service.proto | 162 ++++ protosV2/src/main/proto/netmap/types.proto | 323 +++++++ protosV2/src/main/proto/object/service.proto | 816 ++++++++++++++++++ protosV2/src/main/proto/object/types.proto | 266 ++++++ protosV2/src/main/proto/refs/types.proto | 150 ++++ protosV2/src/main/proto/session/service.proto | 69 ++ protosV2/src/main/proto/session/types.proto | 238 +++++ protosV2/src/main/proto/status/types.proto | 157 ++++ protosV2/src/main/proto/tombstone/types.proto | 27 + 80 files changed, 6315 insertions(+) create mode 100644 .gitignore create mode 100644 CONTRIBUTING.md create mode 100644 client/pom.xml create mode 100644 client/src/main/java/info/FrostFS/sdk/Client.java create mode 100644 client/src/main/java/info/FrostFS/sdk/GrpcClient.java create mode 100644 client/src/main/java/info/FrostFS/sdk/RequestConstructor.java create mode 100644 client/src/main/java/info/FrostFS/sdk/RequestSigner.java create mode 100644 client/src/main/java/info/FrostFS/sdk/Verifier.java create mode 100644 client/src/main/java/info/FrostFS/sdk/services/ContainerClient.java create mode 100644 client/src/main/java/info/FrostFS/sdk/services/ObjectClient.java create mode 100644 client/src/main/java/info/FrostFS/sdk/services/impl/ContainerService.java create mode 100644 client/src/main/java/info/FrostFS/sdk/services/impl/FrostFSClient.java create mode 100644 client/src/main/java/info/FrostFS/sdk/services/impl/NetmapService.java create mode 100644 client/src/main/java/info/FrostFS/sdk/services/impl/ObjectReader.java create mode 100644 client/src/main/java/info/FrostFS/sdk/services/impl/ObjectService.java create mode 100644 client/src/main/java/info/FrostFS/sdk/services/impl/ObjectWriter.java create mode 100644 client/src/main/java/info/FrostFS/sdk/services/impl/SearchReader.java create mode 100644 client/src/main/java/info/FrostFS/sdk/services/impl/SessionService.java create mode 100644 cryptography/pom.xml create mode 100644 cryptography/src/main/java/info/FrostFS/sdk/ArrayHelper.java create mode 100644 cryptography/src/main/java/info/FrostFS/sdk/Base58.java create mode 100644 cryptography/src/main/java/info/FrostFS/sdk/Helper.java create mode 100644 cryptography/src/main/java/info/FrostFS/sdk/KeyExtension.java create mode 100644 modelsV2/pom.xml create mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/UUIDExtension.java create mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/constants/XHeaderConst.java create mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/enums/BasicAcl.java create mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/enums/NodeState.java create mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/enums/ObjectMatchType.java create mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/enums/ObjectType.java create mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/enums/StatusCode.java create mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/jdo/Constants.java create mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/jdo/Container.java create mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/jdo/ContainerId.java create mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/jdo/MetaHeader.java create mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/jdo/ObjectAttribute.java create mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/jdo/ObjectFilter.java create mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/jdo/ObjectFrostFs.java create mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/jdo/ObjectHeader.java create mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/jdo/ObjectId.java create mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/jdo/OwnerId.java create mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/jdo/Status.java create mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/jdo/Version.java create mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/jdo/netmap/NodeInfo.java create mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/jdo/netmap/PlacementPolicy.java create mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/jdo/netmap/Replica.java create mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/mappers/ContainerIdMapper.java create mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/mappers/ContainerMapper.java create mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/mappers/MetaHeaderMapper.java create mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/mappers/ObjectAttributeMapper.java create mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/mappers/ObjectFilterMapper.java create mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/mappers/ObjectHeaderMapper.java create mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/mappers/ObjectIdMapper.java create mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/mappers/ObjectMapper.java create mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/mappers/OwnerIdMapper.java create mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/mappers/StatusMapper.java create mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/mappers/VersionMapper.java create mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/mappers/netmap/NodeInfoMapper.java create mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/mappers/netmap/PlacementPolicyMapper.java create mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/mappers/netmap/ReplicaMapper.java create mode 100644 pom.xml create mode 100644 protosV2/pom.xml create mode 100644 protosV2/src/main/proto/accounting/service.proto create mode 100644 protosV2/src/main/proto/accounting/types.proto create mode 100644 protosV2/src/main/proto/acl/types.proto create mode 100644 protosV2/src/main/proto/apemanager/service.proto create mode 100644 protosV2/src/main/proto/apemanager/types.proto create mode 100644 protosV2/src/main/proto/container/service.proto create mode 100644 protosV2/src/main/proto/container/types.proto create mode 100644 protosV2/src/main/proto/lock/types.proto create mode 100644 protosV2/src/main/proto/netmap/service.proto create mode 100644 protosV2/src/main/proto/netmap/types.proto create mode 100644 protosV2/src/main/proto/object/service.proto create mode 100644 protosV2/src/main/proto/object/types.proto create mode 100644 protosV2/src/main/proto/refs/types.proto create mode 100644 protosV2/src/main/proto/session/service.proto create mode 100644 protosV2/src/main/proto/session/types.proto create mode 100644 protosV2/src/main/proto/status/types.proto create mode 100644 protosV2/src/main/proto/tombstone/types.proto diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c3f0616 --- /dev/null +++ b/.gitignore @@ -0,0 +1,40 @@ +### Maven ### +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +.idea/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..6538ca1 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,156 @@ +# Contribution guide + +First, thank you for contributing! We love and encourage pull requests from +everyone. Please follow the guidelines: + +- Check the open [issues](https://git.frostfs.info/TrueCloudLab/frostfs-sdk-java/issues) and + [pull requests](https://git.frostfs.info/TrueCloudLab/frostfs-sdk-java/pulls) for existing + discussions. + +- Open an issue first, to discuss a new feature or enhancement. + +- Write tests and make sure the test suite passes locally and on CI. + +- Open a pull request and reference the relevant issue(s). + +- Make sure your commits are logically separated and have good comments + explaining the details of your change. + +- After receiving a feedback, amend your commits or add new ones as + appropriate. + +- **Have fun!** + +## Development Workflow + +Start by forking the `frostfs-sdk-java` repository, make changes in a branch and then +send a pull request. We encourage pull requests to discuss code changes. Here +are the steps in details: + +### Set up your git repository +Fork [FrostFS S3 Gateway +upstream](https://git.frostfs.info/repo/fork/346) source repository +to your own personal repository. Copy the URL of your fork (you will need it for +the `git clone` command below). + +```sh +$ git clone https://git.frostfs.info//frostfs-sdk-java.git +``` + +### Set up git remote as ``upstream`` +```sh +$ cd frostfs-sdk-java +$ git remote add upstream https://git.frostfs.info/TrueCloudLab/frostfs-sdk-java.git +$ git fetch upstream +$ git merge upstream/master +... +``` + +### Create your feature branch +Before making code changes, make sure you create a separate branch for these +changes. Maybe you will find it convenient to name a branch in +`/-` format. + +``` +$ git checkout -b feature/123-something_awesome +``` + +### Test your changes +After your code changes, make sure + +- To add test cases for the new code. +- To run `mvn clean verify` +- To squash your commits into a single commit or a series of logically separated + commits with `git rebase -i`. It's okay to force update your pull request. +- To run `mvn clean package` successfully. + +### Commit changes +After verification, commit your changes. There is a [great +post](https://chris.beams.io/posts/git-commit/) on how to write useful commit +messages. Try following this template: + +``` +[#Issue] Summary + +Description + + + + +``` + +``` +$ git commit -ams '[#123] Add some feature' +``` + +### Push to the branch +Push your locally committed changes to the remote origin (your fork) +``` +$ git push origin feature/123-something_awesome +``` + +### Create a Pull Request +Pull requests can be created via Forgejo. Refer to [this +document](https://docs.codeberg.org/collaborating/pull-requests-and-git-flow/) for +detailed steps on how to create a pull request. After a Pull Request gets peer +reviewed and approved, it will be merged. + +## DCO Sign off + +All authors to the project retain copyright to their work. However, to ensure +that they are only submitting work that they have rights to, we require +everyone to acknowledge this by signing their work. + +Any copyright notices in this repository should specify the authors as "the +contributors". + +To sign your work, just add a line like this at the end of your commit message: + +``` +Signed-off-by: Samii Sakisaka +``` + +This can be easily done with the `--signoff` option to `git commit`. + +By doing this you state that you can certify the following (from [The Developer +Certificate of Origin](https://developercertificate.org/)): + +``` +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +1 Letterman Drive +Suite D4700 +San Francisco, CA, 94129 + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. +``` \ No newline at end of file diff --git a/README.md b/README.md index 9f37b2a..26697ca 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,94 @@ # frostfs-sdk-java Java implementation of FrostFS SDK + +## Prerequisites + +### Get the key for your wallet + +1. Get the address +```bash +cat | jq .accounts[0].address | tr -d '"' +``` + +2. Get the key +```bash +neo-go wallet export -w -d +``` + +## Example usage + +### Container + +```java +import info.FrostFS.sdk.enums.BasicAcl; +import info.FrostFS.sdk.jdo.Container; +import info.FrostFS.sdk.jdo.netmap.PlacementPolicy; +import info.FrostFS.sdk.jdo.netmap.Replica; +import info.FrostFS.sdk.services.impl.FrostFSClient; + +public class ContainerExample { + + public void example() { + Client client = new Client( < your_key >); + GrpcClient grpcClient = new GrpcClient( < your_host >); + FrostFSClient frostFSClient = new FrostFSClient(grpcClient, client); + + // Create container + var placementPolicy = new PlacementPolicy(true, new Replica[]{new Replica(1)}); + var containerId = frostFSClient.createContainerAsync(new Container(BasicAcl.PUBLIC_RW, placementPolicy)); + + // Get container + var container = frostFSClient.getContainerAsync(containerId); + + // List containers + var containerIds = frostFSClient.listContainersAsync(); + + // Delete container + frostFSClient.deleteContainerAsync(containerId); + } +} +``` + +### Object + +```java +import info.FrostFS.sdk.enums.ObjectType; +import info.FrostFS.sdk.jdo.ContainerId; +import info.FrostFS.sdk.jdo.ObjectAttribute; +import info.FrostFS.sdk.jdo.ObjectFilter; +import info.FrostFS.sdk.jdo.ObjectHeader; +import info.FrostFS.sdk.services.impl.FrostFSClient; + +import java.io.FileInputStream; +import java.io.IOException; + +public class ObjectExample { + + public void example() { + Client client = new Client( < your_key >); + GrpcClient grpcClient = new GrpcClient( < your_host >); + FrostFSClient frostFSClient = new FrostFSClient(grpcClient, client); + + // Put object + info.FrostFS.sdk.jdo.ObjectId objectId; + try (FileInputStream fis = new FileInputStream("cat.jpg")) { + var cat = new ObjectHeader( + containerId, ObjectType.REGULAR, new ObjectAttribute[]{new ObjectAttribute("Filename", "cat.jpg")} + ); + objectId = frostFSClient.putObjectAsync(cat, fis); + } catch (IOException e) { + throw new RuntimeException(e); + } + + // Get object + var obj = frostFSClient.getObjectAsync(containerId, objectId); + + // Get object header + var objectHeader = frostFSClient.getObjectHeadAsync(containerId, objectId); + + // Search regular objects + var objectIds = frostFSClient.searchObjectsAsync(containerId, ObjectFilter.RootFilter()); + } +} +``` \ No newline at end of file diff --git a/client/pom.xml b/client/pom.xml new file mode 100644 index 0000000..f748035 --- /dev/null +++ b/client/pom.xml @@ -0,0 +1,48 @@ + + + 4.0.0 + + info.FrostFS.sdk + FrostFS-sdk-java + 0.1.0 + + + client + + + 11 + 11 + UTF-8 + + + + + info.FrostFS.sdk + cryptography + 0.1.0 + + + info.FrostFS.sdk + protosV2 + 0.1.0 + + + info.FrostFS.sdk + modelsV2 + 0.1.0 + + + org.apache.logging.log4j + log4j-api + 2.7 + + + org.apache.logging.log4j + log4j-slf4j-impl + 2.7 + + + + \ No newline at end of file diff --git a/client/src/main/java/info/FrostFS/sdk/Client.java b/client/src/main/java/info/FrostFS/sdk/Client.java new file mode 100644 index 0000000..2d254b9 --- /dev/null +++ b/client/src/main/java/info/FrostFS/sdk/Client.java @@ -0,0 +1,36 @@ +package info.FrostFS.sdk; + +import info.FrostFS.sdk.jdo.OwnerId; +import info.FrostFS.sdk.jdo.Version; + +import static info.FrostFS.sdk.KeyExtension.getPrivateKeyFromWIF; +import static info.FrostFS.sdk.KeyExtension.loadPublicKey; + +public class Client { + private final OwnerId ownerId; + private final Version version = new Version(2, 13); + private final byte[] privateKey; + private final byte[] publicKey; + + public Client(String key) { + this.privateKey = getPrivateKeyFromWIF(key); + this.publicKey = loadPublicKey(privateKey); + this.ownerId = OwnerId.fromKey(publicKey); + } + + public OwnerId getOwnerId() { + return ownerId; + } + + public Version getVersion() { + return version; + } + + public byte[] getPrivateKey() { + return privateKey; + } + + public byte[] getPublicKey() { + return publicKey; + } +} diff --git a/client/src/main/java/info/FrostFS/sdk/GrpcClient.java b/client/src/main/java/info/FrostFS/sdk/GrpcClient.java new file mode 100644 index 0000000..0b32277 --- /dev/null +++ b/client/src/main/java/info/FrostFS/sdk/GrpcClient.java @@ -0,0 +1,89 @@ +package info.FrostFS.sdk; + +import frostFS.container.ContainerServiceGrpc; +import frostFS.netmap.NetmapServiceGrpc; +import frostFS.object.ObjectServiceGrpc; +import frostFS.session.SessionServiceGrpc; +import io.grpc.Channel; +import io.grpc.netty.GrpcSslContexts; +import io.grpc.netty.NettyChannelBuilder; +import io.netty.handler.ssl.util.InsecureTrustManagerFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.net.ssl.SSLException; +import java.net.URI; +import java.net.URISyntaxException; + +public class GrpcClient { + private static final Logger log = LoggerFactory.getLogger(GrpcClient.class); + + private final ContainerServiceGrpc.ContainerServiceBlockingStub containerServiceBlockingClient; + private final NetmapServiceGrpc.NetmapServiceBlockingStub netmapServiceBlockingClient; + private final ObjectServiceGrpc.ObjectServiceBlockingStub objectServiceBlockingClient; + private final ObjectServiceGrpc.ObjectServiceStub objectServiceClient; + private final SessionServiceGrpc.SessionServiceBlockingStub sessionServiceBlockingClient; + + public GrpcClient(String host) { + Channel channel = initGrpcChannel(host); + this.containerServiceBlockingClient = ContainerServiceGrpc.newBlockingStub(channel); + this.netmapServiceBlockingClient = NetmapServiceGrpc.newBlockingStub(channel); + this.objectServiceBlockingClient = ObjectServiceGrpc.newBlockingStub(channel); + this.objectServiceClient = ObjectServiceGrpc.newStub(channel); + this.sessionServiceBlockingClient = SessionServiceGrpc.newBlockingStub(channel); + } + + public static Channel initGrpcChannel(String host) { + URI uri; + try { + uri = new URI(host); + } catch (URISyntaxException exp) { + var message = String.format("Host %s has invalid format. Error: %s", host, exp.getMessage()); + log.error(message); + throw new IllegalArgumentException(message); + } + + var channelBuilder = NettyChannelBuilder.forAddress(uri.getHost(), uri.getPort()) + .usePlaintext(); + + switch (uri.getScheme()) { + case "https": + try { + channelBuilder.sslContext( + GrpcSslContexts.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build() + ); + } catch (SSLException e) { + throw new RuntimeException(e); + } + break; + case "http": + break; + default: + var message = String.format("Host %s has invalid URI scheme: %s", host, uri.getScheme()); + log.error(message); + throw new IllegalArgumentException(message); + } + + return channelBuilder.build(); + } + + public ContainerServiceGrpc.ContainerServiceBlockingStub getContainerServiceBlockingClient() { + return containerServiceBlockingClient; + } + + public NetmapServiceGrpc.NetmapServiceBlockingStub getNetmapServiceBlockingClient() { + return netmapServiceBlockingClient; + } + + public ObjectServiceGrpc.ObjectServiceBlockingStub getObjectServiceBlockingClient() { + return objectServiceBlockingClient; + } + + public ObjectServiceGrpc.ObjectServiceStub getObjectServiceClient() { + return objectServiceClient; + } + + public SessionServiceGrpc.SessionServiceBlockingStub getSessionServiceBlockingClient() { + return sessionServiceBlockingClient; + } +} diff --git a/client/src/main/java/info/FrostFS/sdk/RequestConstructor.java b/client/src/main/java/info/FrostFS/sdk/RequestConstructor.java new file mode 100644 index 0000000..304a878 --- /dev/null +++ b/client/src/main/java/info/FrostFS/sdk/RequestConstructor.java @@ -0,0 +1,50 @@ +package info.FrostFS.sdk; + +import com.google.protobuf.AbstractMessage; +import frostFS.session.Types; +import info.FrostFS.sdk.jdo.MetaHeader; +import info.FrostFS.sdk.mappers.MetaHeaderMapper; + +import static info.FrostFS.sdk.RequestSigner.signMessagePart; +import static java.util.Objects.isNull; + +public class RequestConstructor { + + public static void addMetaHeader(AbstractMessage.Builder request) { + addMetaHeader(request, null); + } + + public static void addMetaHeader(AbstractMessage.Builder request, Types.RequestMetaHeader metaHeader) { + if (isNull(metaHeader) || metaHeader.getSerializedSize() == 0) { + metaHeader = MetaHeaderMapper.toGrpcMessage(MetaHeader.getDefault()); + var field = request.getDescriptorForType().findFieldByName("meta_header"); + request.setField(field, metaHeader); + } + } + + public static void addObjectSessionToken(AbstractMessage.Builder request, + Types.SessionToken sessionToken, + frostFS.refs.Types.ContainerID cid, + frostFS.refs.Types.ObjectID oid, + Types.ObjectSessionContext.Verb verb, + byte[] publicKey, byte[] privateKey) { + var headerField = request.getDescriptorForType().findFieldByName("meta_header"); + var header = (Types.RequestMetaHeader) request.getField(headerField); + if (header.getSessionToken().getSerializedSize() > 0) { + return; + } + + var ctx = Types.ObjectSessionContext.newBuilder() + .setTarget(Types.ObjectSessionContext.Target.newBuilder().setContainer(cid).addObjects(oid).build()) + .setVerb(verb) + .build(); + + var body = sessionToken.getBody().toBuilder().setObject(ctx).build(); + sessionToken = sessionToken.toBuilder() + .setSignature(signMessagePart(publicKey, privateKey, body)) + .setBody(body) + .build(); + + request.setField(headerField, header.toBuilder().setSessionToken(sessionToken).build()); + } +} diff --git a/client/src/main/java/info/FrostFS/sdk/RequestSigner.java b/client/src/main/java/info/FrostFS/sdk/RequestSigner.java new file mode 100644 index 0000000..264d52a --- /dev/null +++ b/client/src/main/java/info/FrostFS/sdk/RequestSigner.java @@ -0,0 +1,114 @@ +package info.FrostFS.sdk; + +import com.google.protobuf.AbstractMessage; +import com.google.protobuf.ByteString; +import frostFS.session.Types; +import org.apache.commons.codec.digest.DigestUtils; +import org.bouncycastle.asn1.sec.SECNamedCurves; +import org.bouncycastle.asn1.sec.SECObjectIdentifiers; +import org.bouncycastle.crypto.params.ECDomainParameters; +import org.bouncycastle.crypto.params.ECPrivateKeyParameters; +import org.bouncycastle.crypto.signers.ECDSASigner; +import org.bouncycastle.crypto.signers.HMacDSAKCalculator; + +import java.math.BigInteger; +import java.security.Signature; + +import static info.FrostFS.sdk.KeyExtension.loadPrivateKey; +import static org.bouncycastle.crypto.util.DigestFactory.createSHA256; +import static org.bouncycastle.util.BigIntegers.asUnsignedByteArray; + +public class RequestSigner { + public static final int RFC6979_SIGNATURE_SIZE = 64; + + public static byte[] signData(byte[] privateKey, byte[] data) { + var hash = new byte[65]; + hash[0] = 0x04; + try { + Signature signature = Signature.getInstance("NONEwithECDSAinP1363Format"); + signature.initSign(loadPrivateKey(privateKey)); + signature.update(DigestUtils.sha512(data)); + byte[] sig = signature.sign(); + System.arraycopy(sig, 0, hash, 1, sig.length); + } catch (Exception exp) { + throw new RuntimeException(exp); + } + + return hash; + } + + public static byte[] signRFC6979(byte[] privateKey, byte[] data) { + var digest = createSHA256(); + var secp256R1 = SECNamedCurves.getByOID(SECObjectIdentifiers.secp256r1); + + var ecParameters = new ECDomainParameters(secp256R1.getCurve(), secp256R1.getG(), secp256R1.getN()); + var ecPrivateKey = new ECPrivateKeyParameters(new BigInteger(1, privateKey), ecParameters); + var signer = new ECDSASigner(new HMacDSAKCalculator(digest)); + var hash = new byte[digest.getDigestSize()]; + + digest.update(data, 0, data.length); + digest.doFinal(hash, 0); + signer.init(true, ecPrivateKey); + + var rs = signer.generateSignature(hash); + var rBytes = asUnsignedByteArray(rs[0]); + var sBytes = asUnsignedByteArray(rs[1]); + + var signature = new byte[RFC6979_SIGNATURE_SIZE]; + var index = RFC6979_SIGNATURE_SIZE / 2 - rBytes.length; + System.arraycopy(rBytes, 0, signature, index, rBytes.length); + index = RFC6979_SIGNATURE_SIZE - sBytes.length; + System.arraycopy(sBytes, 0, signature, index, sBytes.length); + return signature; + } + + public static frostFS.refs.Types.SignatureRFC6979 signRFC6979(byte[] publicKey, byte[] privateKey, AbstractMessage message) { + return frostFS.refs.Types.SignatureRFC6979.newBuilder() + .setKey(ByteString.copyFrom(publicKey)) + .setSign(ByteString.copyFrom(signRFC6979(privateKey, message.toByteArray()))) + .build(); + } + + public static frostFS.refs.Types.SignatureRFC6979 signRFC6979(byte[] publicKey, byte[] privateKey, ByteString data) { + return frostFS.refs.Types.SignatureRFC6979.newBuilder() + .setKey(ByteString.copyFrom(publicKey)) + .setSign(ByteString.copyFrom(signRFC6979(privateKey, data.toByteArray()))) + .build(); + } + + public static frostFS.refs.Types.Signature signMessagePart(byte[] publicKey, byte[] privateKey, AbstractMessage data) { + var data2Sign = data.getSerializedSize() == 0 ? new byte[]{} : data.toByteArray(); + + return frostFS.refs.Types.Signature.newBuilder() + .setKey(ByteString.copyFrom(publicKey)) + .setSign(ByteString.copyFrom(signData(privateKey, data2Sign))) + .build(); + } + + public static void sign(AbstractMessage.Builder request, byte[] publicKey, byte[] privateKey) { + var meta = (AbstractMessage) request.getField(request.getDescriptorForType().findFieldByName("meta_header")); + var body = (AbstractMessage) request.getField(request.getDescriptorForType().findFieldByName("body")); + var verify = (AbstractMessage) request.getField(request.getDescriptorForType().findFieldByName("verify_header")); + + AbstractMessage.Builder verifyBuilder; + if (verify instanceof Types.RequestVerificationHeader) { + verifyBuilder = Types.RequestVerificationHeader.newBuilder(); + } else if (verify instanceof Types.ResponseVerificationHeader) { + verifyBuilder = Types.ResponseVerificationHeader.newBuilder(); + } else { + throw new IllegalArgumentException("Unsopported message type"); + } + + var verifyOrigin = (AbstractMessage) verify.getField(verify.getDescriptorForType().findFieldByName("origin")); + if (verifyOrigin.getSerializedSize() == 0) { + verifyBuilder.setField(verifyBuilder.getDescriptorForType().findFieldByName("body_signature"), signMessagePart(publicKey, privateKey, body)); + } else { + verifyBuilder.setField(verifyBuilder.getDescriptorForType().findFieldByName("origin"), verifyOrigin); + } + + verifyBuilder.setField(verifyBuilder.getDescriptorForType().findFieldByName("meta_signature"), signMessagePart(publicKey, privateKey, meta)); + verifyBuilder.setField(verifyBuilder.getDescriptorForType().findFieldByName("origin_signature"), signMessagePart(publicKey, privateKey, verifyOrigin)); + + request.setField(request.getDescriptorForType().findFieldByName("verify_header"), verifyBuilder.build()); + } +} diff --git a/client/src/main/java/info/FrostFS/sdk/Verifier.java b/client/src/main/java/info/FrostFS/sdk/Verifier.java new file mode 100644 index 0000000..657be82 --- /dev/null +++ b/client/src/main/java/info/FrostFS/sdk/Verifier.java @@ -0,0 +1,116 @@ +package info.FrostFS.sdk; + +import com.google.protobuf.AbstractMessage; +import frostFS.refs.Types; +import info.FrostFS.sdk.mappers.StatusMapper; +import org.apache.commons.codec.digest.DigestUtils; +import org.bouncycastle.asn1.sec.SECNamedCurves; +import org.bouncycastle.asn1.sec.SECObjectIdentifiers; +import org.bouncycastle.crypto.params.ECDomainParameters; +import org.bouncycastle.crypto.params.ECPublicKeyParameters; +import org.bouncycastle.crypto.signers.ECDSASigner; +import org.bouncycastle.crypto.signers.HMacDSAKCalculator; + +import java.math.BigInteger; +import java.security.PublicKey; +import java.security.Signature; +import java.util.Arrays; + +import static info.FrostFS.sdk.KeyExtension.getPublicKeyByPublic; +import static java.util.Objects.isNull; +import static org.bouncycastle.crypto.util.DigestFactory.createSHA256; +import static org.bouncycastle.util.BigIntegers.fromUnsignedByteArray; + +public class Verifier { + public static final int RFC6979_SIGNATURE_SIZE = 64; + + public static boolean verifyRFC6979(Types.SignatureRFC6979 signature, AbstractMessage message) { + return verifyRFC6979(signature.getKey().toByteArray(), message.toByteArray(), signature.getSign().toByteArray()); + } + + public static boolean verifyRFC6979(byte[] publicKey, byte[] data, byte[] sig) { + if (isNull(publicKey) || isNull(data) || isNull(sig)) return false; + + var rs = decodeSignature(sig); + var digest = createSHA256(); + var signer = new ECDSASigner(new HMacDSAKCalculator(digest)); + var secp256R1 = SECNamedCurves.getByOID(SECObjectIdentifiers.secp256r1); + var ecParameters = new ECDomainParameters(secp256R1.getCurve(), secp256R1.getG(), secp256R1.getN()); + var ecPublicKey = new ECPublicKeyParameters(secp256R1.getCurve().decodePoint(publicKey), ecParameters); + var hash = new byte[digest.getDigestSize()]; + digest.update(data, 0, data.length); + digest.doFinal(hash, 0); + signer.init(false, ecPublicKey); + return signer.verifySignature(hash, rs[0], rs[1]); + } + + private static BigInteger[] decodeSignature(byte[] sig) { + if (sig.length != RFC6979_SIGNATURE_SIZE) { + throw new IllegalArgumentException( + String.format("Wrong signature size. Expected length=%s, actual=%s", + RFC6979_SIGNATURE_SIZE, sig.length) + ); + } + + var rs = new BigInteger[2]; + + rs[0] = fromUnsignedByteArray(Arrays.copyOfRange(sig, 0, (RFC6979_SIGNATURE_SIZE / 2) - 1)); + rs[1] = fromUnsignedByteArray(Arrays.copyOfRange(sig, RFC6979_SIGNATURE_SIZE / 2, RFC6979_SIGNATURE_SIZE - 1)); + return rs; + } + + public static void checkResponse(AbstractMessage response) { + if (!verify(response)) { + throw new IllegalArgumentException("Invalid response"); + } + var metaHeader = (frostFS.session.Types.ResponseMetaHeader) response + .getField(response.getDescriptorForType().findFieldByName("meta_header")); + var status = StatusMapper.toModel(metaHeader.getStatus()); + if (!status.isSuccess()) { + throw new IllegalArgumentException(status.toString()); + } + } + + public static boolean verify(AbstractMessage message) { + var body = (AbstractMessage) message.getField(message.getDescriptorForType().findFieldByName("body")); + var metaHeader = (frostFS.session.Types.ResponseMetaHeader) + message.getField(message.getDescriptorForType().findFieldByName("meta_header")); + var verifyHeader = (frostFS.session.Types.ResponseVerificationHeader) + message.getField(message.getDescriptorForType().findFieldByName("verify_header")); + + return verifyMatryoshkaLevel(body, metaHeader, verifyHeader); + } + + public static boolean verifyMatryoshkaLevel(AbstractMessage body, + frostFS.session.Types.ResponseMetaHeader meta, + frostFS.session.Types.ResponseVerificationHeader verification) { + if (!verifyMessagePart(verification.getMetaSignature(), meta)) return false; + var origin = verification.getOrigin(); + if (!verifyMessagePart(verification.getOriginSignature(), origin)) return false; + if (origin.getSerializedSize() == 0) { + return verifyMessagePart(verification.getBodySignature(), body); + } + return verification.getBodySignature().getSerializedSize() == 0 + && verifyMatryoshkaLevel(body, meta.getOrigin(), origin); + } + + public static boolean verifyMessagePart(Types.Signature sig, AbstractMessage data) { + if (sig.getSerializedSize() == 0 || sig.getKey().isEmpty() || sig.getSign().isEmpty()) return false; + + var publicKey = getPublicKeyByPublic(sig.getKey().toByteArray()); + + var data2Verify = data.getSerializedSize() == 0 ? new byte[]{} : data.toByteArray(); + return verifyData(publicKey, data2Verify, sig.getSign().toByteArray()); + } + + public static boolean verifyData(PublicKey publicKey, byte[] data, byte[] sig) { + try { + Signature signature = Signature.getInstance("NONEwithECDSAinP1363Format"); + signature.initVerify(publicKey); + signature.update(DigestUtils.sha512(data)); + return signature.verify(Arrays.copyOfRange(sig, 1, sig.length)); + } catch (Exception exp) { + throw new RuntimeException(exp); + } + } +} diff --git a/client/src/main/java/info/FrostFS/sdk/services/ContainerClient.java b/client/src/main/java/info/FrostFS/sdk/services/ContainerClient.java new file mode 100644 index 0000000..3cdb8c2 --- /dev/null +++ b/client/src/main/java/info/FrostFS/sdk/services/ContainerClient.java @@ -0,0 +1,16 @@ +package info.FrostFS.sdk.services; + +import info.FrostFS.sdk.jdo.Container; +import info.FrostFS.sdk.jdo.ContainerId; + +import java.util.List; + +public interface ContainerClient { + Container getContainerAsync(ContainerId cid); + + List listContainersAsync(); + + ContainerId createContainerAsync(Container container); + + void deleteContainerAsync(ContainerId cid); +} diff --git a/client/src/main/java/info/FrostFS/sdk/services/ObjectClient.java b/client/src/main/java/info/FrostFS/sdk/services/ObjectClient.java new file mode 100644 index 0000000..682d52a --- /dev/null +++ b/client/src/main/java/info/FrostFS/sdk/services/ObjectClient.java @@ -0,0 +1,19 @@ +package info.FrostFS.sdk.services; + +import info.FrostFS.sdk.jdo.*; + +import java.io.FileInputStream; + +public interface ObjectClient { + ObjectHeader getObjectHeadAsync(ContainerId containerId, ObjectId objectId); + + ObjectFrostFs getObjectAsync(ContainerId containerId, ObjectId objectId); + + ObjectId putObjectAsync(ObjectHeader header, FileInputStream payload); + + ObjectId putObjectAsync(ObjectHeader header, byte[] payload); + + void deleteObjectAsync(ContainerId containerId, ObjectId objectId); + + Iterable searchObjectsAsync(ContainerId cid, ObjectFilter... filters); +} diff --git a/client/src/main/java/info/FrostFS/sdk/services/impl/ContainerService.java b/client/src/main/java/info/FrostFS/sdk/services/impl/ContainerService.java new file mode 100644 index 0000000..782c218 --- /dev/null +++ b/client/src/main/java/info/FrostFS/sdk/services/impl/ContainerService.java @@ -0,0 +1,107 @@ +package info.FrostFS.sdk.services.impl; + +import frostFS.container.ContainerServiceGrpc; +import frostFS.container.Service; +import info.FrostFS.sdk.Client; +import info.FrostFS.sdk.Verifier; +import info.FrostFS.sdk.jdo.Container; +import info.FrostFS.sdk.jdo.ContainerId; +import info.FrostFS.sdk.mappers.ContainerIdMapper; +import info.FrostFS.sdk.mappers.ContainerMapper; +import info.FrostFS.sdk.mappers.OwnerIdMapper; +import info.FrostFS.sdk.mappers.VersionMapper; +import info.FrostFS.sdk.services.ContainerClient; + +import java.util.List; +import java.util.stream.Collectors; + +import static info.FrostFS.sdk.RequestConstructor.addMetaHeader; +import static info.FrostFS.sdk.RequestSigner.sign; +import static info.FrostFS.sdk.RequestSigner.signRFC6979; + +public class ContainerService implements ContainerClient { + private final ContainerServiceGrpc.ContainerServiceBlockingStub containerServiceAsyncClient; + private final Client client; + + public ContainerService(ContainerServiceGrpc.ContainerServiceBlockingStub containerServiceAsyncClient, Client client) { + this.containerServiceAsyncClient = containerServiceAsyncClient; + this.client = client; + } + + public Container getContainerAsync(ContainerId cid) { + var request = Service.GetRequest.newBuilder() + .setBody( + Service.GetRequest.Body.newBuilder().setContainerId(ContainerIdMapper.toGrpcMessage(cid)).build() + ); + + addMetaHeader(request, null); + sign(request, client.getPublicKey(), client.getPrivateKey()); + + var response = containerServiceAsyncClient.get(request.build()); + + Verifier.checkResponse(response); + return ContainerMapper.toModel(response.getBody().getContainer()); + } + + public List listContainersAsync() { + var request = Service.ListRequest.newBuilder() + .setBody( + Service.ListRequest.Body.newBuilder().setOwnerId(OwnerIdMapper.toGrpcMessage(client.getOwnerId())).build() + ); + + addMetaHeader(request, null); + sign(request, client.getPublicKey(), client.getPrivateKey()); + + var response = containerServiceAsyncClient.list(request.build()); + + Verifier.checkResponse(response); + + return response.getBody().getContainerIdsList().stream() + .map(cid -> ContainerId.fromHash(cid.getValue().toByteArray())) + .collect(Collectors.toList()); + } + + public ContainerId createContainerAsync(Container container) { + var grpcContainer = ContainerMapper.toGrpcMessage(container); + + grpcContainer = grpcContainer.toBuilder() + .setOwnerId(OwnerIdMapper.toGrpcMessage(client.getOwnerId())) + .setVersion(VersionMapper.toGrpcMessage(client.getVersion())) + .build(); + + var request = Service.PutRequest.newBuilder() + .setBody( + Service.PutRequest.Body.newBuilder() + .setContainer(grpcContainer) + .setSignature(signRFC6979(client.getPublicKey(), client.getPrivateKey(), grpcContainer)) + .build() + ); + + addMetaHeader(request, null); + sign(request, client.getPublicKey(), client.getPrivateKey()); + + var response = containerServiceAsyncClient.put(request.build()); + + Verifier.checkResponse(response); + return ContainerId.fromHash(response.getBody().getContainerId().getValue().toByteArray()); + } + + public void deleteContainerAsync(ContainerId cid) { + var grpcContainerId = ContainerIdMapper.toGrpcMessage(cid); + + var request = Service.DeleteRequest.newBuilder() + .setBody( + Service.DeleteRequest.Body.newBuilder() + .setContainerId(grpcContainerId) + .setSignature(signRFC6979(client.getPublicKey(), client.getPrivateKey(), grpcContainerId.getValue())) + .build() + ); + + addMetaHeader(request, null); + sign(request, client.getPublicKey(), client.getPrivateKey()); + + var response = containerServiceAsyncClient.delete(request.build()); + + Verifier.checkResponse(response); + } +} diff --git a/client/src/main/java/info/FrostFS/sdk/services/impl/FrostFSClient.java b/client/src/main/java/info/FrostFS/sdk/services/impl/FrostFSClient.java new file mode 100644 index 0000000..a720d4c --- /dev/null +++ b/client/src/main/java/info/FrostFS/sdk/services/impl/FrostFSClient.java @@ -0,0 +1,85 @@ +package info.FrostFS.sdk.services.impl; + +import info.FrostFS.sdk.Client; +import info.FrostFS.sdk.GrpcClient; +import info.FrostFS.sdk.jdo.*; +import info.FrostFS.sdk.services.ContainerClient; +import info.FrostFS.sdk.services.ObjectClient; + +import java.io.FileInputStream; +import java.util.List; + +public class FrostFSClient implements ContainerClient, ObjectClient { + private final ContainerService containerService; + private final NetmapService netmapService; + private final ObjectService objectService; + + public FrostFSClient(GrpcClient grpcClient, Client client) { + this.containerService = new ContainerService(grpcClient.getContainerServiceBlockingClient(), client); + this.netmapService = new NetmapService(grpcClient.getNetmapServiceBlockingClient(), client); + SessionService sessionService = new SessionService(grpcClient.getSessionServiceBlockingClient(), client); + this.objectService = new ObjectService( + grpcClient.getObjectServiceBlockingClient(), grpcClient.getObjectServiceClient(), sessionService, client + ); + checkFrostFsVersionSupport(client); + } + + private void checkFrostFsVersionSupport(Client client) { + var localNodeInfo = netmapService.getLocalNodeInfoAsync(); + if (!localNodeInfo.getVersion().isSupported(client.getVersion())) { + var msg = String.format("FrostFS %s is not supported.", localNodeInfo.getVersion()); + System.out.println(msg); + throw new IllegalArgumentException(msg); + } + } + + @Override + public Container getContainerAsync(ContainerId cid) { + return containerService.getContainerAsync(cid); + } + + @Override + public List listContainersAsync() { + return containerService.listContainersAsync(); + } + + @Override + public ContainerId createContainerAsync(Container container) { + return containerService.createContainerAsync(container); + } + + @Override + public void deleteContainerAsync(ContainerId cid) { + containerService.deleteContainerAsync(cid); + } + + @Override + public ObjectHeader getObjectHeadAsync(ContainerId containerId, ObjectId objectId) { + return objectService.getObjectHeadAsync(containerId, objectId); + } + + @Override + public ObjectFrostFs getObjectAsync(ContainerId containerId, ObjectId objectId) { + return objectService.getObjectAsync(containerId, objectId); + } + + @Override + public ObjectId putObjectAsync(ObjectHeader header, FileInputStream payload) { + return objectService.putObjectAsync(header, payload); + } + + @Override + public ObjectId putObjectAsync(ObjectHeader header, byte[] payload) { + return objectService.putObjectAsync(header, payload); + } + + @Override + public void deleteObjectAsync(ContainerId containerId, ObjectId objectId) { + objectService.deleteObjectAsync(containerId, objectId); + } + + @Override + public Iterable searchObjectsAsync(ContainerId cid, ObjectFilter... filters) { + return objectService.searchObjectsAsync(cid, filters); + } +} diff --git a/client/src/main/java/info/FrostFS/sdk/services/impl/NetmapService.java b/client/src/main/java/info/FrostFS/sdk/services/impl/NetmapService.java new file mode 100644 index 0000000..eafc40f --- /dev/null +++ b/client/src/main/java/info/FrostFS/sdk/services/impl/NetmapService.java @@ -0,0 +1,42 @@ +package info.FrostFS.sdk.services.impl; + +import frostFS.netmap.NetmapServiceGrpc; +import frostFS.netmap.Service; +import info.FrostFS.sdk.Client; +import info.FrostFS.sdk.jdo.netmap.NodeInfo; +import info.FrostFS.sdk.mappers.netmap.NodeInfoMapper; + +import static info.FrostFS.sdk.RequestConstructor.addMetaHeader; +import static info.FrostFS.sdk.RequestSigner.sign; + +public class NetmapService { + private final NetmapServiceGrpc.NetmapServiceBlockingStub netmapServiceAsyncClient; + private final Client client; + + public NetmapService(NetmapServiceGrpc.NetmapServiceBlockingStub netmapServiceAsyncClient, Client client) { + this.netmapServiceAsyncClient = netmapServiceAsyncClient; + this.client = client; + } + + public NodeInfo getLocalNodeInfoAsync() { + var request = Service.LocalNodeInfoRequest.newBuilder() + .setBody(Service.LocalNodeInfoRequest.Body.newBuilder().build()); + + addMetaHeader(request, null); + sign(request, client.getPublicKey(), client.getPrivateKey()); + + var response = netmapServiceAsyncClient.localNodeInfo(request.build()); + + return NodeInfoMapper.toModel(response.getBody()); + } + + public Service.NetworkInfoResponse getNetworkInfoAsync() { + var request = Service.NetworkInfoRequest.newBuilder() + .setBody(Service.NetworkInfoRequest.Body.newBuilder().build()); + + addMetaHeader(request, null); + sign(request, client.getPublicKey(), client.getPrivateKey()); + + return netmapServiceAsyncClient.networkInfo(request.build()); + } +} diff --git a/client/src/main/java/info/FrostFS/sdk/services/impl/ObjectReader.java b/client/src/main/java/info/FrostFS/sdk/services/impl/ObjectReader.java new file mode 100644 index 0000000..8350bd7 --- /dev/null +++ b/client/src/main/java/info/FrostFS/sdk/services/impl/ObjectReader.java @@ -0,0 +1,48 @@ +package info.FrostFS.sdk.services.impl; + +import frostFS.object.Service; +import frostFS.object.Types; +import info.FrostFS.sdk.Verifier; + +import java.util.Iterator; + +public class ObjectReader { + public Iterator call; + + public ObjectReader(Iterator call) { + this.call = call; + } + + public Types.Object readHeader() { + if (!call.hasNext()) { + throw new IllegalArgumentException("unexpected end of stream"); + } + + var response = call.next(); + Verifier.checkResponse(response); + + if (response.getBody().getObjectPartCase().getNumber() != Service.GetResponse.Body.INIT_FIELD_NUMBER) { + throw new IllegalArgumentException("unexpected message type"); + } + + return Types.Object.newBuilder() + .setObjectId(response.getBody().getInit().getObjectId()) + .setHeader(response.getBody().getInit().getHeader()) + .build(); + } + + public byte[] readChunk() { + if (!call.hasNext()) { + return null; + } + + var response = call.next(); + Verifier.checkResponse(response); + + if (response.getBody().getObjectPartCase().getNumber() != Service.GetResponse.Body.CHUNK_FIELD_NUMBER) { + throw new IllegalArgumentException("unexpected message type"); + } + + return response.getBody().getChunk().toByteArray(); + } +} diff --git a/client/src/main/java/info/FrostFS/sdk/services/impl/ObjectService.java b/client/src/main/java/info/FrostFS/sdk/services/impl/ObjectService.java new file mode 100644 index 0000000..f90fe84 --- /dev/null +++ b/client/src/main/java/info/FrostFS/sdk/services/impl/ObjectService.java @@ -0,0 +1,257 @@ +package info.FrostFS.sdk.services.impl; + +import com.google.common.collect.Iterables; +import com.google.protobuf.ByteString; +import frostFS.object.ObjectServiceGrpc; +import frostFS.object.Service; +import frostFS.refs.Types; +import info.FrostFS.sdk.Client; +import info.FrostFS.sdk.Verifier; +import info.FrostFS.sdk.jdo.*; +import info.FrostFS.sdk.mappers.*; +import info.FrostFS.sdk.services.ObjectClient; + +import java.io.ByteArrayInputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static info.FrostFS.sdk.Helper.getSha256; +import static info.FrostFS.sdk.RequestConstructor.addMetaHeader; +import static info.FrostFS.sdk.RequestConstructor.addObjectSessionToken; +import static info.FrostFS.sdk.RequestSigner.sign; +import static java.util.Objects.nonNull; + +public class ObjectService implements ObjectClient { + private final ObjectServiceGrpc.ObjectServiceBlockingStub objectServiceBlockingClient; + private final ObjectServiceGrpc.ObjectServiceStub objectServiceClient; + private final SessionService sessionService; + private final Client client; + + + public ObjectService(ObjectServiceGrpc.ObjectServiceBlockingStub objectServiceAsyncClient, + ObjectServiceGrpc.ObjectServiceStub objectServiceClient, + SessionService sessionService, + Client client) { + this.objectServiceBlockingClient = objectServiceAsyncClient; + this.objectServiceClient = objectServiceClient; + this.client = client; + this.sessionService = sessionService; + } + + @Override + public ObjectHeader getObjectHeadAsync(ContainerId cid, ObjectId oid) { + var request = Service.HeadRequest.newBuilder() + .setBody( + Service.HeadRequest.Body.newBuilder() + .setAddress( + Types.Address.newBuilder() + .setContainerId(ContainerIdMapper.toGrpcMessage(cid)) + .setObjectId(ObjectIdMapper.toGrpcMessage(oid)) + .build() + ).build() + ); + + addMetaHeader(request, null); + sign(request, client.getPublicKey(), client.getPrivateKey()); + + var response = objectServiceBlockingClient.head(request.build()); + Verifier.checkResponse(response); + + return ObjectHeaderMapper.toModel(response.getBody().getHeader().getHeader()); + } + + @Override + public ObjectFrostFs getObjectAsync(ContainerId cid, ObjectId oid) { + var sessionToken = sessionService.createSessionAsync(-1); + + var request = Service.GetRequest.newBuilder() + .setBody( + Service.GetRequest.Body.newBuilder() + .setAddress( + Types.Address.newBuilder() + .setContainerId(ContainerIdMapper.toGrpcMessage(cid)) + .setObjectId(ObjectIdMapper.toGrpcMessage(oid)) + .build() + ) + .setRaw(false) + .build() + ); + + addMetaHeader(request, null); + addObjectSessionToken( + request, sessionToken, ContainerIdMapper.toGrpcMessage(cid), ObjectIdMapper.toGrpcMessage(oid), + frostFS.session.Types.ObjectSessionContext.Verb.GET, client.getPublicKey(), client.getPrivateKey() + ); + sign(request, client.getPublicKey(), client.getPrivateKey()); + + var obj = getObject(request.build()); + + return ObjectMapper.toModel(obj); + } + + @Override + public void deleteObjectAsync(ContainerId cid, ObjectId oid) { + var request = Service.DeleteRequest.newBuilder() + .setBody( + Service.DeleteRequest.Body.newBuilder() + .setAddress( + Types.Address.newBuilder() + .setContainerId(ContainerIdMapper.toGrpcMessage(cid)) + .setObjectId(ObjectIdMapper.toGrpcMessage(oid)) + .build() + ) + .build()); + + addMetaHeader(request, null); + sign(request, client.getPublicKey(), client.getPrivateKey()); + + var response = objectServiceBlockingClient.delete(request.build()); + Verifier.checkResponse(response); + } + + @Override + public ObjectId putObjectAsync(ObjectHeader header, FileInputStream payload) { + return putObject(header, payload); + } + + @Override + public ObjectId putObjectAsync(ObjectHeader header, byte[] payload) { + return putObject(header, new ByteArrayInputStream(payload)); + } + + @Override + public Iterable searchObjectsAsync(ContainerId cid, ObjectFilter... filters) { + var body = Service.SearchRequest.Body.newBuilder() + .setContainerId(ContainerIdMapper.toGrpcMessage(cid)) + .setVersion(1); + + for (ObjectFilter filter : filters) { + body.addFilters(ObjectFilterMapper.toGrpcMessage(filter)); + } + + var request = Service.SearchRequest.newBuilder() + .setBody(body.build()); + + addMetaHeader(request, null); + sign(request, client.getPublicKey(), client.getPrivateKey()); + + var objectsIds = searchObjects(request.build()); + + return Iterables.transform(objectsIds, input -> ObjectId.fromHash(input.getValue().toByteArray())); + } + + private frostFS.object.Types.Object getObject(Service.GetRequest request) { + var iterator = getObjectInit(request); + var obj = iterator.readHeader(); + var payload = new byte[Math.toIntExact(obj.getHeader().getPayloadLength())]; + var offset = 0; + var chunk = iterator.readChunk(); + + while (nonNull(chunk)) { + System.arraycopy(chunk, 0, payload, offset, chunk.length); + offset += chunk.length; + chunk = iterator.readChunk(); + } + + return obj.toBuilder().setPayload(ByteString.copyFrom(payload)).build(); + } + + private ObjectReader getObjectInit(Service.GetRequest initRequest) { + if (initRequest.getSerializedSize() == 0) { + throw new IllegalArgumentException(initRequest.getClass().getName()); + } + + return new ObjectReader(objectServiceBlockingClient.get(initRequest)); + } + + private ObjectId putObject(ObjectHeader header, InputStream payload) { + var sessionToken = sessionService.createSessionAsync(-1); + var hdr = ObjectHeaderMapper.toGrpcMessage(header); + + hdr = hdr.toBuilder() + .setOwnerId(OwnerIdMapper.toGrpcMessage(client.getOwnerId())) + .setVersion(VersionMapper.toGrpcMessage(client.getVersion())) + .build(); + + var oid = Types.ObjectID.newBuilder().setValue(getSha256(hdr)).build(); + + var request = Service.PutRequest.newBuilder() + .setBody( + Service.PutRequest.Body.newBuilder() + .setInit( + Service.PutRequest.Body.Init.newBuilder().setHeader(hdr).build() + ).build() + ); + + addMetaHeader(request, null); + addObjectSessionToken( + request, sessionToken, hdr.getContainerId(), oid, frostFS.session.Types.ObjectSessionContext.Verb.PUT, + client.getPublicKey(), client.getPrivateKey() + ); + sign(request, client.getPublicKey(), client.getPrivateKey()); + + + var writer = putObjectInit(request.build()); + + var buffer = new byte[Constants.OBJECT_CHUNK_SIZE]; + int bufferLength = 0; + try { + bufferLength = payload.readNBytes(buffer, 0, Constants.OBJECT_CHUNK_SIZE); + while (bufferLength > 0) { + request.setBody( + Service.PutRequest.Body.newBuilder() + .setChunk(ByteString.copyFrom(Arrays.copyOfRange(buffer, 0, bufferLength))) + .build() + ) + .clearVerifyHeader(); + sign(request, client.getPublicKey(), client.getPrivateKey()); + writer.write(request.build()); + bufferLength = payload.readNBytes(buffer, 0, Constants.OBJECT_CHUNK_SIZE); + } + } catch ( + IOException e) { + throw new RuntimeException(e); + } + + var response = writer.complete(); + Verifier.checkResponse(response); + + return ObjectId.fromHash(response.getBody().getObjectId().getValue().toByteArray()); + } + + private ObjectWriter putObjectInit(Service.PutRequest initRequest) { + if (initRequest.getSerializedSize() == 0) { + throw new IllegalArgumentException(initRequest.getClass().getName()); + } + + ObjectWriter writer = new ObjectWriter(objectServiceClient); + writer.write(initRequest); + return writer; + } + + private Iterable searchObjects(Service.SearchRequest request) { + var reader = getSearchReader(request); + var ids = reader.read(); + List result = new ArrayList<>(); + + while (nonNull(ids) && !ids.isEmpty()) { + result.addAll(ids); + ids = reader.read(); + } + + return result; + } + + private SearchReader getSearchReader(Service.SearchRequest initRequest) { + if (initRequest.getSerializedSize() == 0) { + throw new IllegalArgumentException(initRequest.getClass().getName()); + } + + return new SearchReader(objectServiceBlockingClient.search(initRequest)); + } + +} diff --git a/client/src/main/java/info/FrostFS/sdk/services/impl/ObjectWriter.java b/client/src/main/java/info/FrostFS/sdk/services/impl/ObjectWriter.java new file mode 100644 index 0000000..01ae85e --- /dev/null +++ b/client/src/main/java/info/FrostFS/sdk/services/impl/ObjectWriter.java @@ -0,0 +1,59 @@ +package info.FrostFS.sdk.services.impl; + +import frostFS.object.ObjectServiceGrpc; +import frostFS.object.Service; +import io.grpc.stub.StreamObserver; + +import static java.util.Objects.isNull; + +public class ObjectWriter { + private final StreamObserver requestObserver; + private final PutResponseCallback responseObserver; + + public ObjectWriter(ObjectServiceGrpc.ObjectServiceStub objectServiceStub) { + PutResponseCallback responseObserver = new PutResponseCallback(); + + this.responseObserver = responseObserver; + this.requestObserver = objectServiceStub.put(responseObserver); + } + + public void write(Service.PutRequest request) { + if (isNull(request)) { + throw new IllegalArgumentException(); + } + + requestObserver.onNext(request); + } + + public Service.PutResponse complete() { + requestObserver.onCompleted(); + + while (isNull(responseObserver.getResponse())) { + System.out.println("Waiting response"); + } + + return responseObserver.getResponse(); + } + + private static class PutResponseCallback implements StreamObserver { + private Service.PutResponse response; + + @Override + public void onNext(Service.PutResponse putResponse) { + this.response = putResponse; + } + + @Override + public void onError(Throwable throwable) { + throw new RuntimeException(throwable); + } + + @Override + public void onCompleted() { + } + + public Service.PutResponse getResponse() { + return response; + } + } +} diff --git a/client/src/main/java/info/FrostFS/sdk/services/impl/SearchReader.java b/client/src/main/java/info/FrostFS/sdk/services/impl/SearchReader.java new file mode 100644 index 0000000..b0d3620 --- /dev/null +++ b/client/src/main/java/info/FrostFS/sdk/services/impl/SearchReader.java @@ -0,0 +1,26 @@ +package info.FrostFS.sdk.services.impl; + +import frostFS.object.Service; +import info.FrostFS.sdk.Verifier; + +import java.util.Iterator; +import java.util.List; + +public class SearchReader { + public Iterator call; + + public SearchReader(Iterator call) { + this.call = call; + } + + public List read() { + if (!call.hasNext()) { + return null; + } + + var response = call.next(); + Verifier.checkResponse(response); + + return response.getBody().getIdListList(); + } +} diff --git a/client/src/main/java/info/FrostFS/sdk/services/impl/SessionService.java b/client/src/main/java/info/FrostFS/sdk/services/impl/SessionService.java new file mode 100644 index 0000000..832dc52 --- /dev/null +++ b/client/src/main/java/info/FrostFS/sdk/services/impl/SessionService.java @@ -0,0 +1,55 @@ +package info.FrostFS.sdk.services.impl; + +import frostFS.session.Service; +import frostFS.session.SessionServiceGrpc; +import frostFS.session.Types; +import info.FrostFS.sdk.Client; +import info.FrostFS.sdk.mappers.OwnerIdMapper; + +import static info.FrostFS.sdk.RequestConstructor.addMetaHeader; +import static info.FrostFS.sdk.RequestSigner.sign; + +public class SessionService { + private final SessionServiceGrpc.SessionServiceBlockingStub sessionServiceAsyncClient; + private final Client client; + + public SessionService(SessionServiceGrpc.SessionServiceBlockingStub sessionServiceAsyncClient, Client client) { + this.sessionServiceAsyncClient = sessionServiceAsyncClient; + this.client = client; + } + + protected Types.SessionToken createSessionAsync(long expiration) { + var request = Service.CreateRequest.newBuilder() + .setBody( + Service.CreateRequest.Body.newBuilder() + .setOwnerId(OwnerIdMapper.toGrpcMessage(client.getOwnerId())) + .setExpiration(expiration).build() + ); + + addMetaHeader(request, null); + sign(request, client.getPublicKey(), client.getPrivateKey()); + + return createSession(request.build()); + } + + private Types.SessionToken createSession(Service.CreateRequest request) { + var resp = sessionServiceAsyncClient.create(request); + + var lifetime = Types.SessionToken.Body.TokenLifetime.newBuilder() + .setExp(request.getBody().getExpiration()) + .setIat(resp.getMetaHeader().getEpoch()) + .setNbf(resp.getMetaHeader().getEpoch()) + .build(); + + var body = Types.SessionToken.Body.newBuilder() + .setId(resp.getBody().getId()) + .setSessionKey(resp.getBody().getSessionKey()) + .setOwnerId(request.getBody().getOwnerId()) + .setLifetime(lifetime) + .build(); + + return Types.SessionToken.newBuilder() + .setBody(body) + .build(); + } +} diff --git a/cryptography/pom.xml b/cryptography/pom.xml new file mode 100644 index 0000000..9af2880 --- /dev/null +++ b/cryptography/pom.xml @@ -0,0 +1,44 @@ + + + 4.0.0 + + info.FrostFS.sdk + FrostFS-sdk-java + 0.1.0 + + + cryptography + + + 11 + 11 + UTF-8 + 3.23.0 + + + + + commons-codec + commons-codec + 1.17.0 + + + com.google.protobuf + protobuf-java + ${protobuf.version} + + + org.bouncycastle + bcprov-jdk18on + 1.78.1 + + + org.bouncycastle + bcpkix-jdk18on + 1.78.1 + + + + \ No newline at end of file diff --git a/cryptography/src/main/java/info/FrostFS/sdk/ArrayHelper.java b/cryptography/src/main/java/info/FrostFS/sdk/ArrayHelper.java new file mode 100644 index 0000000..5274f60 --- /dev/null +++ b/cryptography/src/main/java/info/FrostFS/sdk/ArrayHelper.java @@ -0,0 +1,12 @@ +package info.FrostFS.sdk; + +public class ArrayHelper { + + public static byte[] concat(byte[] startArray, byte[] endArray) { + byte[] result = new byte[startArray.length + endArray.length]; + + System.arraycopy(startArray, 0, result, 0, startArray.length); + System.arraycopy(endArray, 0, result, startArray.length, endArray.length); + return result; + } +} diff --git a/cryptography/src/main/java/info/FrostFS/sdk/Base58.java b/cryptography/src/main/java/info/FrostFS/sdk/Base58.java new file mode 100644 index 0000000..9fe27af --- /dev/null +++ b/cryptography/src/main/java/info/FrostFS/sdk/Base58.java @@ -0,0 +1,127 @@ +package info.FrostFS.sdk; + +import java.util.Arrays; + +import static info.FrostFS.sdk.ArrayHelper.concat; +import static info.FrostFS.sdk.Helper.getSha256; +import static java.util.Objects.isNull; + +public class Base58 { + public static final char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray(); + private static final char ENCODED_ZERO = ALPHABET[0]; + private static final int[] INDEXES = new int[128]; + + static { + Arrays.fill(INDEXES, -1); + for (int i = 0; i < ALPHABET.length; i++) { + INDEXES[ALPHABET[i]] = i; + } + } + + public static byte[] base58CheckDecode(String input) { + if (isNull(input) || input.isEmpty()) { + throw new IllegalArgumentException("Input value is missing"); + } + + byte[] buffer = decode(input); + if (buffer.length < 4) { + throw new IllegalArgumentException(); + } + + byte[] decode = Arrays.copyOfRange(buffer, 0, buffer.length - 4); + byte[] checksum = getSha256(getSha256(decode)); + if (!Arrays.equals( + Arrays.copyOfRange(buffer, buffer.length - 4, buffer.length), + Arrays.copyOfRange(checksum, 0, 4) + )) { + throw new IllegalArgumentException(); + } + + return decode; + } + + public static String base58CheckEncode(byte[] data) { + byte[] checksum = getSha256(getSha256(data)); + var buffer = concat(data, Arrays.copyOfRange(checksum, 0, 4)); + var ret = encode(buffer); + return ret; + } + + public static String encode(byte[] input) { + if (input.length == 0) { + return ""; + } + // Count leading zeros. + int zeros = 0; + while (zeros < input.length && input[zeros] == 0) { + ++zeros; + } + // Convert base-256 digits to base-58 digits (plus conversion to ASCII characters) + input = Arrays.copyOf(input, input.length); // since we modify it in-place + char[] encoded = new char[input.length * 2]; // upper bound + int outputStart = encoded.length; + for (int inputStart = zeros; inputStart < input.length; ) { + encoded[--outputStart] = ALPHABET[divmod(input, inputStart, 256, 58)]; + if (input[inputStart] == 0) { + ++inputStart; // optimization - skip leading zeros + } + } + // Preserve exactly as many leading encoded zeros in output as there were leading zeros in input. + while (outputStart < encoded.length && encoded[outputStart] == ENCODED_ZERO) { + ++outputStart; + } + while (--zeros >= 0) { + encoded[--outputStart] = ENCODED_ZERO; + } + // Return encoded string (including encoded leading zeros). + return new String(encoded, outputStart, encoded.length - outputStart); + } + + public static byte[] decode(String input) { + if (input.isEmpty()) { + return new byte[0]; + } + // Convert the base58-encoded ASCII chars to a base58 byte sequence (base58 digits). + byte[] input58 = new byte[input.length()]; + for (int i = 0; i < input.length(); ++i) { + char c = input.charAt(i); + int digit = c < 128 ? INDEXES[c] : -1; + if (digit < 0) { + throw new IllegalArgumentException(String.format("Invalid character in Base58: 0x%04x", (int) c)); + } + input58[i] = (byte) digit; + } + // Count leading zeros. + int zeros = 0; + while (zeros < input58.length && input58[zeros] == 0) { + ++zeros; + } + // Convert base-58 digits to base-256 digits. + byte[] decoded = new byte[input.length()]; + int outputStart = decoded.length; + for (int inputStart = zeros; inputStart < input58.length; ) { + decoded[--outputStart] = divmod(input58, inputStart, 58, 256); + if (input58[inputStart] == 0) { + ++inputStart; // optimization - skip leading zeros + } + } + // Ignore extra leading zeroes that were added during the calculation. + while (outputStart < decoded.length && decoded[outputStart] == 0) { + ++outputStart; + } + // Return decoded data (including original number of leading zeros). + return Arrays.copyOfRange(decoded, outputStart - zeros, decoded.length); + } + + private static byte divmod(byte[] number, int firstDigit, int base, int divisor) { + // this is just long division which accounts for the base of the input digits + int remainder = 0; + for (int i = firstDigit; i < number.length; i++) { + int digit = (int) number[i] & 0xFF; + int temp = remainder * base + digit; + number[i] = (byte) (temp / divisor); + remainder = temp % divisor; + } + return (byte) remainder; + } +} diff --git a/cryptography/src/main/java/info/FrostFS/sdk/Helper.java b/cryptography/src/main/java/info/FrostFS/sdk/Helper.java new file mode 100644 index 0000000..e4e1460 --- /dev/null +++ b/cryptography/src/main/java/info/FrostFS/sdk/Helper.java @@ -0,0 +1,36 @@ +package info.FrostFS.sdk; + +import com.google.protobuf.AbstractMessage; +import com.google.protobuf.ByteString; +import org.bouncycastle.crypto.digests.RIPEMD160Digest; + +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public class Helper { + + public static byte[] getRIPEMD160(byte[] value) { + var hash = new byte[20]; + var digest = new RIPEMD160Digest(); + digest.update(value, 0, value.length); + digest.doFinal(hash, 0); + return hash; + } + + public static byte[] getSha256(byte[] value) { + try { + return MessageDigest.getInstance("SHA-256").digest(value); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } + + public static ByteString getSha256(AbstractMessage value) { + return ByteString.copyFrom(getSha256(value.toByteArray())); + } + + public static String пуеHexString(byte[] array) { + return String.format("%0" + (array.length << 1) + "x", new BigInteger(1, array)); + } +} diff --git a/cryptography/src/main/java/info/FrostFS/sdk/KeyExtension.java b/cryptography/src/main/java/info/FrostFS/sdk/KeyExtension.java new file mode 100644 index 0000000..b8c5ab6 --- /dev/null +++ b/cryptography/src/main/java/info/FrostFS/sdk/KeyExtension.java @@ -0,0 +1,161 @@ +package info.FrostFS.sdk; + +import org.bouncycastle.asn1.sec.SECNamedCurves; +import org.bouncycastle.asn1.sec.SECObjectIdentifiers; +import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.crypto.params.ECDomainParameters; +import org.bouncycastle.crypto.params.ECPrivateKeyParameters; +import org.bouncycastle.crypto.params.ECPublicKeyParameters; +import org.bouncycastle.jce.spec.ECNamedCurveSpec; +import org.bouncycastle.math.ec.ECPoint; + +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.StandardCharsets; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPrivateKeySpec; +import java.security.spec.ECPublicKeySpec; +import java.security.spec.InvalidKeySpecException; +import java.util.Arrays; + +import static info.FrostFS.sdk.Helper.getRIPEMD160; +import static info.FrostFS.sdk.Helper.getSha256; +import static org.bouncycastle.util.BigIntegers.fromUnsignedByteArray; + +public class KeyExtension { + public static final byte NEO_ADDRESS_VERSION = 0x35; + private static final int COMPRESSED_PUBLIC_KEY_LENGTH = 33; + private static final int UNCOMPRESSED_PUBLIC_KEY_LENGTH = 65; + + private static final int CHECK_SIG_DESCRIPTOR = ByteBuffer + .wrap(getSha256("System.Crypto.CheckSig".getBytes(StandardCharsets.US_ASCII))) + .order(ByteOrder.LITTLE_ENDIAN).getInt(); + + + public static byte[] compress(byte[] publicKey) { + if (publicKey.length != UNCOMPRESSED_PUBLIC_KEY_LENGTH) { + throw new IllegalArgumentException( + String.format("Compress argument isn't uncompressed public key. Expected length=%s, actual=%s", + UNCOMPRESSED_PUBLIC_KEY_LENGTH, publicKey.length) + ); + } + + var secp256R1 = SECNamedCurves.getByOID(SECObjectIdentifiers.secp256r1); + var point = secp256R1.getCurve().decodePoint(publicKey); + return point.getEncoded(true); + } + + public static byte[] getPrivateKeyFromWIF(String wif) { + var data = Base58.base58CheckDecode(wif); + return Arrays.copyOfRange(data, 1, data.length - 1); + } + + public static byte[] loadPublicKey(byte[] privateKey) { + X9ECParameters params = SECNamedCurves.getByOID(SECObjectIdentifiers.secp256r1); + ECDomainParameters domain = new ECDomainParameters(params.getCurve(), params.getG(), params.getN(), params.getH()); + ECPoint q = domain.getG().multiply(new BigInteger(1, privateKey)); + ECPublicKeyParameters publicParams = new ECPublicKeyParameters(q, domain); + return publicParams.getQ().getEncoded(true); + } + + public static PrivateKey loadPrivateKey(byte[] privateKey) { + X9ECParameters params = SECNamedCurves.getByOID(SECObjectIdentifiers.secp256r1); + ECDomainParameters domain = new ECDomainParameters(params.getCurve(), params.getG(), params.getN(), params.getH()); + ECPrivateKeyParameters ecParams = new ECPrivateKeyParameters(fromUnsignedByteArray(privateKey), domain); + + ECParameterSpec ecParameterSpec = new ECNamedCurveSpec( + "secp256r1", params.getCurve(), params.getG(), params.getN(), params.getH() + ); + ECPrivateKeySpec privateKeySpec = new ECPrivateKeySpec(ecParams.getD(), ecParameterSpec); + try { + KeyFactory kf = KeyFactory.getInstance("EC"); + return kf.generatePrivate(privateKeySpec); + } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { + throw new RuntimeException(e); + } + } + + public static PublicKey getPublicKeyByPublic(byte[] publicKey) { + if (publicKey.length != COMPRESSED_PUBLIC_KEY_LENGTH) { + throw new IllegalArgumentException( + String.format("Decompress argument isn't compressed public key. Expected length=%s, actual=%s", + COMPRESSED_PUBLIC_KEY_LENGTH, publicKey.length) + ); + } + + X9ECParameters secp256R1 = SECNamedCurves.getByOID(SECObjectIdentifiers.secp256r1); + ECDomainParameters domain = new ECDomainParameters(secp256R1.getCurve(), secp256R1.getG(), secp256R1.getN(), secp256R1.getH()); + var ecPoint = secp256R1.getCurve().decodePoint(publicKey); + var publicParams = new ECPublicKeyParameters(ecPoint, domain); + java.security.spec.ECPoint point = new java.security.spec.ECPoint( + publicParams.getQ().getRawXCoord().toBigInteger(), publicParams.getQ().getRawYCoord().toBigInteger() + ); + ECParameterSpec ecParameterSpec = new ECNamedCurveSpec( + "secp256r1", secp256R1.getCurve(), secp256R1.getG(), secp256R1.getN(), secp256R1.getH(), secp256R1.getSeed() + ); + ECPublicKeySpec publicKeySpec = new ECPublicKeySpec(point, ecParameterSpec); + + try { + KeyFactory kf = KeyFactory.getInstance("EC"); + return kf.generatePublic(publicKeySpec); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + + public static byte[] getScriptHash(byte[] publicKey) { + var script = createSignatureRedeemScript(publicKey); + + return getRIPEMD160(getSha256(script)); + } + + public static String publicKeyToAddress(byte[] publicKey) { + if (publicKey.length != COMPRESSED_PUBLIC_KEY_LENGTH) { + throw new IllegalArgumentException( + String.format("PublicKey isn't encoded compressed public key. Expected length=%s, actual=%s", + COMPRESSED_PUBLIC_KEY_LENGTH, publicKey.length) + ); + } + + return toAddress(getScriptHash(publicKey), NEO_ADDRESS_VERSION); + } + + private static String toAddress(byte[] scriptHash, byte version) { + byte[] data = new byte[21]; + data[0] = version; + System.arraycopy(scriptHash, 0, data, 1, scriptHash.length); + return Base58.base58CheckEncode(data); + } + + private static byte[] getBytes(int value) { + byte[] buffer = new byte[4]; + + for (int i = 0; i < buffer.length; i++) { + buffer[i] = (byte) (value >> i * 8); + } + + return buffer; + } + + private static byte[] createSignatureRedeemScript(byte[] publicKey) { + if (publicKey.length != COMPRESSED_PUBLIC_KEY_LENGTH) { + throw new IllegalArgumentException( + String.format("PublicKey isn't encoded compressed public key. Expected length=%s, actual=%s", + COMPRESSED_PUBLIC_KEY_LENGTH, publicKey.length) + ); + } + + var script = new byte[]{0x0c, COMPRESSED_PUBLIC_KEY_LENGTH}; //PUSHDATA1 33 + + script = ArrayHelper.concat(script, publicKey); + script = ArrayHelper.concat(script, new byte[]{0x41}); //SYSCALL + script = ArrayHelper.concat(script, getBytes(CHECK_SIG_DESCRIPTOR)); //Neo_Crypto_CheckSig + return script; + } +} diff --git a/modelsV2/pom.xml b/modelsV2/pom.xml new file mode 100644 index 0000000..9093e71 --- /dev/null +++ b/modelsV2/pom.xml @@ -0,0 +1,38 @@ + + + 4.0.0 + + info.FrostFS.sdk + FrostFS-sdk-java + 0.1.0 + + + modelsV2 + + + 11 + 11 + UTF-8 + + + + + org.apache.commons + commons-lang3 + 3.14.0 + + + info.FrostFS.sdk + cryptography + 0.1.0 + + + info.FrostFS.sdk + protosV2 + 0.1.0 + + + + \ No newline at end of file diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/UUIDExtension.java b/modelsV2/src/main/java/info/FrostFS/sdk/UUIDExtension.java new file mode 100644 index 0000000..df146ec --- /dev/null +++ b/modelsV2/src/main/java/info/FrostFS/sdk/UUIDExtension.java @@ -0,0 +1,21 @@ +package info.FrostFS.sdk; + +import java.nio.ByteBuffer; +import java.util.UUID; + +public class UUIDExtension { + + public static UUID asUuid(byte[] bytes) { + ByteBuffer bb = ByteBuffer.wrap(bytes); + long firstLong = bb.getLong(); + long secondLong = bb.getLong(); + return new UUID(firstLong, secondLong); + } + + public static byte[] asBytes(UUID id) { + ByteBuffer bb = ByteBuffer.allocate(16); + bb.putLong(id.getMostSignificantBits()); + bb.putLong(id.getLeastSignificantBits()); + return bb.array(); + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/constants/XHeaderConst.java b/modelsV2/src/main/java/info/FrostFS/sdk/constants/XHeaderConst.java new file mode 100644 index 0000000..8c435e7 --- /dev/null +++ b/modelsV2/src/main/java/info/FrostFS/sdk/constants/XHeaderConst.java @@ -0,0 +1,7 @@ +package info.FrostFS.sdk.constants; + +public class XHeaderConst { + public static final String RESERVED_XHEADER_PREFIX = "__SYSTEM__"; + public static final String XHEADER_NETMAP_EPOCH = RESERVED_XHEADER_PREFIX + "NETMAP_EPOCH"; + public static final String XHEADER_NETMAP_LOOKUP_DEPTH = RESERVED_XHEADER_PREFIX + "NETMAP_LOOKUP_DEPTH"; +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/enums/BasicAcl.java b/modelsV2/src/main/java/info/FrostFS/sdk/enums/BasicAcl.java new file mode 100644 index 0000000..b454a68 --- /dev/null +++ b/modelsV2/src/main/java/info/FrostFS/sdk/enums/BasicAcl.java @@ -0,0 +1,33 @@ +package info.FrostFS.sdk.enums; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public enum BasicAcl { + PRIVATE(0x1C8C8CCC), + PUBLIC_RO(0x1FBF8CFF), + PUBLIC_RW(0x1FBFBFFF), + PUBLIC_APPEND(0x1FBF9FFF), + ; + + private static final Map ENUM_MAP_BY_VALUE; + + static { + Map map = new HashMap<>(); + for (BasicAcl basicAcl : BasicAcl.values()) { + map.put(basicAcl.value, basicAcl); + } + ENUM_MAP_BY_VALUE = Collections.unmodifiableMap(map); + } + + public final int value; + + BasicAcl(int value) { + this.value = value; + } + + public static BasicAcl get(int value) { + return ENUM_MAP_BY_VALUE.get(value); + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/enums/NodeState.java b/modelsV2/src/main/java/info/FrostFS/sdk/enums/NodeState.java new file mode 100644 index 0000000..e6f1464 --- /dev/null +++ b/modelsV2/src/main/java/info/FrostFS/sdk/enums/NodeState.java @@ -0,0 +1,33 @@ +package info.FrostFS.sdk.enums; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public enum NodeState { + UNSPECIFIED(0), + ONLINE(1), + OFFLINE(2), + MAINTENANCE(3), + ; + + private static final Map ENUM_MAP_BY_VALUE; + + static { + Map map = new HashMap<>(); + for (NodeState nodeState : NodeState.values()) { + map.put(nodeState.value, nodeState); + } + ENUM_MAP_BY_VALUE = Collections.unmodifiableMap(map); + } + + public final int value; + + NodeState(int value) { + this.value = value; + } + + public static NodeState get(int value) { + return ENUM_MAP_BY_VALUE.get(value); + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/enums/ObjectMatchType.java b/modelsV2/src/main/java/info/FrostFS/sdk/enums/ObjectMatchType.java new file mode 100644 index 0000000..385756f --- /dev/null +++ b/modelsV2/src/main/java/info/FrostFS/sdk/enums/ObjectMatchType.java @@ -0,0 +1,16 @@ +package info.FrostFS.sdk.enums; + +public enum ObjectMatchType { + UNSPECIFIED(0), + EQUALS(1), + NOT_EQUALS(2), + KEY_ABSENT(3), + STARTS_WITH(4), + ; + + public final int value; + + ObjectMatchType(int value) { + this.value = value; + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/enums/ObjectType.java b/modelsV2/src/main/java/info/FrostFS/sdk/enums/ObjectType.java new file mode 100644 index 0000000..feb545f --- /dev/null +++ b/modelsV2/src/main/java/info/FrostFS/sdk/enums/ObjectType.java @@ -0,0 +1,32 @@ +package info.FrostFS.sdk.enums; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public enum ObjectType { + REGULAR(0), + TOMBSTONE(1), + LOCK(3), + ; + + private static final Map ENUM_MAP_BY_VALUE; + + static { + Map map = new HashMap<>(); + for (ObjectType objectType : ObjectType.values()) { + map.put(objectType.value, objectType); + } + ENUM_MAP_BY_VALUE = Collections.unmodifiableMap(map); + } + + public final int value; + + ObjectType(int value) { + this.value = value; + } + + public static ObjectType get(int value) { + return ENUM_MAP_BY_VALUE.get(value); + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/enums/StatusCode.java b/modelsV2/src/main/java/info/FrostFS/sdk/enums/StatusCode.java new file mode 100644 index 0000000..4e44421 --- /dev/null +++ b/modelsV2/src/main/java/info/FrostFS/sdk/enums/StatusCode.java @@ -0,0 +1,46 @@ +package info.FrostFS.sdk.enums; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public enum StatusCode { + SUCCESS(0), + INTERNAL(1024), + WRONG_MAGIC_NUMBER(1025), + SIGNATURE_VERIFICATION_FAILURE(1026), + NODE_UNDER_MAINTENANCE(1027), + OBJECT_ACCESS_DENIED(2048), + OBJECT_NOT_FOUND(2049), + OBJECT_LOCKED(2050), + LOCK_NOT_REGULAR_OBJECT(2051), + OBJECT_ALREADY_REMOVED(2052), + OUT_OF_RANGE(2053), + CONTAINER_NOT_FOUND(3072), + E_ACL_NOT_FOUND(3073), + CONTAINER_ACCESS_DENIED(3074), + TOKEN_NOT_FOUND(4096), + TOKEN_EXPIRED(4097), + APE_MANAGER_ACCESS_DENIED(5120), + ; + + private static final Map ENUM_MAP_BY_VALUE; + + static { + Map map = new HashMap<>(); + for (StatusCode statusCode : StatusCode.values()) { + map.put(statusCode.value, statusCode); + } + ENUM_MAP_BY_VALUE = Collections.unmodifiableMap(map); + } + + public final int value; + + StatusCode(int value) { + this.value = value; + } + + public static StatusCode get(int value) { + return ENUM_MAP_BY_VALUE.get(value); + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/Constants.java b/modelsV2/src/main/java/info/FrostFS/sdk/jdo/Constants.java new file mode 100644 index 0000000..2c01d9e --- /dev/null +++ b/modelsV2/src/main/java/info/FrostFS/sdk/jdo/Constants.java @@ -0,0 +1,6 @@ +package info.FrostFS.sdk.jdo; + +public class Constants { + public static final int OBJECT_CHUNK_SIZE = 3 * (1 << 20); + public static final int SHA256_HASH_LENGTH = 32; +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/Container.java b/modelsV2/src/main/java/info/FrostFS/sdk/jdo/Container.java new file mode 100644 index 0000000..38df197 --- /dev/null +++ b/modelsV2/src/main/java/info/FrostFS/sdk/jdo/Container.java @@ -0,0 +1,58 @@ +package info.FrostFS.sdk.jdo; + +import info.FrostFS.sdk.enums.BasicAcl; +import info.FrostFS.sdk.jdo.netmap.PlacementPolicy; + +import java.util.UUID; + +public class Container { + public UUID nonce; + public BasicAcl basicAcl; + public PlacementPolicy placementPolicy; + public Version version; + + public UUID getNonce() { + return nonce; + } + + public void setNonce(UUID nonce) { + this.nonce = nonce; + } + + public BasicAcl getBasicAcl() { + return basicAcl; + } + + public void setBasicAcl(BasicAcl basicAcl) { + this.basicAcl = basicAcl; + } + + public PlacementPolicy getPlacementPolicy() { + return placementPolicy; + } + + public void setPlacementPolicy(PlacementPolicy placementPolicy) { + this.placementPolicy = placementPolicy; + } + + public Version getVersion() { + return version; + } + + public void setVersion(Version version) { + this.version = version; + } + + public Container(BasicAcl basicAcl, PlacementPolicy placementPolicy) { + nonce = UUID.randomUUID(); + this.basicAcl = basicAcl; + this.placementPolicy = placementPolicy; + } + + public Container(BasicAcl basicAcl, PlacementPolicy placementPolicy, UUID nonce, Version version) { + this.nonce = nonce; + this.basicAcl = basicAcl; + this.placementPolicy = placementPolicy; + this.version = version; + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/ContainerId.java b/modelsV2/src/main/java/info/FrostFS/sdk/jdo/ContainerId.java new file mode 100644 index 0000000..309f25b --- /dev/null +++ b/modelsV2/src/main/java/info/FrostFS/sdk/jdo/ContainerId.java @@ -0,0 +1,35 @@ +package info.FrostFS.sdk.jdo; + +import info.FrostFS.sdk.Base58; + +public class ContainerId { + public String value; + + public ContainerId(String value) { + this.value = value; + } + + public static ContainerId fromHash(byte[] hash) { + if (hash.length != Constants.SHA256_HASH_LENGTH) { + throw new IllegalArgumentException("ContainerID must be a sha256 hash."); + } + return new ContainerId(Base58.encode(hash)); + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + @Override + public String toString() { + return value; + } + + public byte[] toHash() { + return Base58.decode(value); + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/MetaHeader.java b/modelsV2/src/main/java/info/FrostFS/sdk/jdo/MetaHeader.java new file mode 100644 index 0000000..5f428b4 --- /dev/null +++ b/modelsV2/src/main/java/info/FrostFS/sdk/jdo/MetaHeader.java @@ -0,0 +1,41 @@ +package info.FrostFS.sdk.jdo; + +public class MetaHeader { + public Version version; + public int epoch; + public int ttl; + + public MetaHeader(Version version, int epoch, int ttl) { + this.version = version; + this.epoch = epoch; + this.ttl = ttl; + } + + public static MetaHeader getDefault() { + return new MetaHeader(new Version(2, 13), 0, 2); + } + + public Version getVersion() { + return version; + } + + public void setVersion(Version version) { + this.version = version; + } + + public int getEpoch() { + return epoch; + } + + public void setEpoch(int epoch) { + this.epoch = epoch; + } + + public int getTtl() { + return ttl; + } + + public void setTtl(int ttl) { + this.ttl = ttl; + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/ObjectAttribute.java b/modelsV2/src/main/java/info/FrostFS/sdk/jdo/ObjectAttribute.java new file mode 100644 index 0000000..1a122f4 --- /dev/null +++ b/modelsV2/src/main/java/info/FrostFS/sdk/jdo/ObjectAttribute.java @@ -0,0 +1,27 @@ +package info.FrostFS.sdk.jdo; + +public class ObjectAttribute { + public String key; + public String value; + + public ObjectAttribute(String key, String value) { + this.key = key; + this.value = value; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/ObjectFilter.java b/modelsV2/src/main/java/info/FrostFS/sdk/jdo/ObjectFilter.java new file mode 100644 index 0000000..0fb1db3 --- /dev/null +++ b/modelsV2/src/main/java/info/FrostFS/sdk/jdo/ObjectFilter.java @@ -0,0 +1,58 @@ +package info.FrostFS.sdk.jdo; + +import info.FrostFS.sdk.enums.ObjectMatchType; + +public class ObjectFilter { + private static final String HEADER_PREFIX = "Object:"; + + public ObjectMatchType matchType; + public String key; + public String value; + + + public ObjectFilter(ObjectMatchType matchType, String key, String value) { + this.matchType = matchType; + this.key = key; + this.value = value; + } + + public static ObjectFilter ObjectIdFilter(ObjectMatchType matchType, ObjectId objectId) { + return new ObjectFilter(matchType, HEADER_PREFIX + "objectID", objectId.value); + } + + public static ObjectFilter OwnerFilter(ObjectMatchType matchType, OwnerId ownerId) { + return new ObjectFilter(matchType, HEADER_PREFIX + "ownerID", ownerId.getValue()); + } + + public static ObjectFilter RootFilter() { + return new ObjectFilter(ObjectMatchType.UNSPECIFIED, HEADER_PREFIX + "ROOT", ""); + } + + public static ObjectFilter VersionFilter(ObjectMatchType matchType, Version version) { + return new ObjectFilter(matchType, HEADER_PREFIX + "version", version.toString()); + } + + public ObjectMatchType getMatchType() { + return matchType; + } + + public void setMatchType(ObjectMatchType matchType) { + this.matchType = matchType; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/ObjectFrostFs.java b/modelsV2/src/main/java/info/FrostFS/sdk/jdo/ObjectFrostFs.java new file mode 100644 index 0000000..c7cc689 --- /dev/null +++ b/modelsV2/src/main/java/info/FrostFS/sdk/jdo/ObjectFrostFs.java @@ -0,0 +1,37 @@ +package info.FrostFS.sdk.jdo; + +public class ObjectFrostFs { + public ObjectHeader header; + public ObjectId objectId; + public byte[] payload; + + public ObjectFrostFs(ObjectHeader header, ObjectId objectId, byte[] payload) { + this.header = header; + this.objectId = objectId; + this.payload = payload; + } + + public ObjectHeader getHeader() { + return header; + } + + public void setHeader(ObjectHeader header) { + this.header = header; + } + + public ObjectId getObjectId() { + return objectId; + } + + public void setObjectId(ObjectId objectId) { + this.objectId = objectId; + } + + public byte[] getPayload() { + return payload; + } + + public void setPayload(byte[] payload) { + this.payload = payload; + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/ObjectHeader.java b/modelsV2/src/main/java/info/FrostFS/sdk/jdo/ObjectHeader.java new file mode 100644 index 0000000..7dc795c --- /dev/null +++ b/modelsV2/src/main/java/info/FrostFS/sdk/jdo/ObjectHeader.java @@ -0,0 +1,71 @@ +package info.FrostFS.sdk.jdo; + +import info.FrostFS.sdk.enums.ObjectType; + +public class ObjectHeader { + public ObjectAttribute[] attributes; + public ContainerId containerId; + public long size; + public ObjectType objectType; + public Version version; + + public ObjectHeader(ContainerId containerId, ObjectType objectType, ObjectAttribute[] attributes, long size, Version version) { + this.attributes = attributes; + this.containerId = containerId; + this.size = size; + this.objectType = objectType; + this.version = version; + } + + public ObjectHeader(ContainerId containerId, ObjectType type, ObjectAttribute[] attributes) { + this.attributes = attributes; + this.containerId = containerId; + this.objectType = type; + } + + public ObjectHeader(ContainerId containerId, ObjectAttribute[] attributes) { + this.attributes = attributes; + this.containerId = containerId; + this.objectType = ObjectType.REGULAR; + } + + public ObjectAttribute[] getAttributes() { + return attributes; + } + + public void setAttributes(ObjectAttribute[] attributes) { + this.attributes = attributes; + } + + public ContainerId getContainerId() { + return containerId; + } + + public void setContainerId(ContainerId containerId) { + this.containerId = containerId; + } + + public long getSize() { + return size; + } + + public void setSize(long size) { + this.size = size; + } + + public ObjectType getObjectType() { + return objectType; + } + + public void setObjectType(ObjectType objectType) { + this.objectType = objectType; + } + + public Version getVersion() { + return version; + } + + public void setVersion(Version version) { + this.version = version; + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/ObjectId.java b/modelsV2/src/main/java/info/FrostFS/sdk/jdo/ObjectId.java new file mode 100644 index 0000000..438d027 --- /dev/null +++ b/modelsV2/src/main/java/info/FrostFS/sdk/jdo/ObjectId.java @@ -0,0 +1,31 @@ +package info.FrostFS.sdk.jdo; + +import info.FrostFS.sdk.Base58; + +public class ObjectId { + public String value; + + public ObjectId(String id) { + this.value = id; + } + + public static ObjectId fromHash(byte[] hash) { + if (hash.length != Constants.SHA256_HASH_LENGTH) { + throw new IllegalArgumentException("ObjectId must be a sha256 hash."); + } + return new ObjectId(Base58.encode(hash)); + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return value; + } + + public byte[] toHash() { + return Base58.decode(value); + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/OwnerId.java b/modelsV2/src/main/java/info/FrostFS/sdk/jdo/OwnerId.java new file mode 100644 index 0000000..723e234 --- /dev/null +++ b/modelsV2/src/main/java/info/FrostFS/sdk/jdo/OwnerId.java @@ -0,0 +1,25 @@ +package info.FrostFS.sdk.jdo; + +import info.FrostFS.sdk.Base58; + +import static info.FrostFS.sdk.KeyExtension.publicKeyToAddress; + +public class OwnerId { + private final String value; + + public OwnerId(String value) { + this.value = value; + } + + public static OwnerId fromKey(byte[] publicKey) { + return new OwnerId(publicKeyToAddress(publicKey)); + } + + public String getValue() { + return value; + } + + public byte[] toHash() { + return Base58.decode(value); + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/Status.java b/modelsV2/src/main/java/info/FrostFS/sdk/jdo/Status.java new file mode 100644 index 0000000..bcb6f08 --- /dev/null +++ b/modelsV2/src/main/java/info/FrostFS/sdk/jdo/Status.java @@ -0,0 +1,45 @@ +package info.FrostFS.sdk.jdo; + +import info.FrostFS.sdk.enums.StatusCode; + +import java.util.Optional; + +public class Status { + public StatusCode code; + public String message; + + public Status(StatusCode code, String message) { + this.code = code; + this.message = Optional.ofNullable(message).orElse(""); + } + + public Status(StatusCode code) { + this.code = code; + this.message = ""; + } + + public StatusCode getCode() { + return code; + } + + public void setCode(StatusCode code) { + this.code = code; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + @Override + public String toString() { + return "Response status: " + code + ". Message: " + message + "."; + } + + public boolean isSuccess() { + return StatusCode.SUCCESS.equals(code); + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/Version.java b/modelsV2/src/main/java/info/FrostFS/sdk/jdo/Version.java new file mode 100644 index 0000000..22cd8c7 --- /dev/null +++ b/modelsV2/src/main/java/info/FrostFS/sdk/jdo/Version.java @@ -0,0 +1,36 @@ +package info.FrostFS.sdk.jdo; + +public class Version { + public int major; + public int minor; + + public int getMajor() { + return major; + } + + public void setMajor(int major) { + this.major = major; + } + + public int getMinor() { + return minor; + } + + public void setMinor(int minor) { + this.minor = minor; + } + + @Override + public String toString() { + return "v" + major + "." + minor; + } + + public Version(int major, int minor) { + this.major = major; + this.minor = minor; + } + + public boolean isSupported(Version version) { + return major == version.getMajor(); + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/netmap/NodeInfo.java b/modelsV2/src/main/java/info/FrostFS/sdk/jdo/netmap/NodeInfo.java new file mode 100644 index 0000000..fe65459 --- /dev/null +++ b/modelsV2/src/main/java/info/FrostFS/sdk/jdo/netmap/NodeInfo.java @@ -0,0 +1,30 @@ +package info.FrostFS.sdk.jdo.netmap; + +import info.FrostFS.sdk.jdo.Version; +import info.FrostFS.sdk.enums.NodeState; + +public class NodeInfo { + public NodeState state; + public Version version; + + public NodeState getState() { + return state; + } + + public void setState(NodeState state) { + this.state = state; + } + + public Version getVersion() { + return version; + } + + public void setVersion(Version version) { + this.version = version; + } + + public NodeInfo(NodeState state, Version version) { + this.state = state; + this.version = version; + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/netmap/PlacementPolicy.java b/modelsV2/src/main/java/info/FrostFS/sdk/jdo/netmap/PlacementPolicy.java new file mode 100644 index 0000000..d32b585 --- /dev/null +++ b/modelsV2/src/main/java/info/FrostFS/sdk/jdo/netmap/PlacementPolicy.java @@ -0,0 +1,27 @@ +package info.FrostFS.sdk.jdo.netmap; + +public class PlacementPolicy { + public Replica[] replicas; + public boolean unique; + + public PlacementPolicy(boolean unique, Replica[] replicas) { + this.replicas = replicas; + this.unique = unique; + } + + public Replica[] getReplicas() { + return replicas; + } + + public void setReplicas(Replica[] replicas) { + this.replicas = replicas; + } + + public boolean isUnique() { + return unique; + } + + public void setUnique(boolean unique) { + this.unique = unique; + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/netmap/Replica.java b/modelsV2/src/main/java/info/FrostFS/sdk/jdo/netmap/Replica.java new file mode 100644 index 0000000..80d1e89 --- /dev/null +++ b/modelsV2/src/main/java/info/FrostFS/sdk/jdo/netmap/Replica.java @@ -0,0 +1,34 @@ +package info.FrostFS.sdk.jdo.netmap; + +import java.util.Optional; + +public class Replica { + public int count; + public String selector; + + public Replica(int count, String selector) { + this.count = count; + this.selector = Optional.ofNullable(selector).orElse(""); + } + + public Replica(int count) { + this.count = count; + this.selector = ""; + } + + public int getCount() { + return count; + } + + public void setCount(int count) { + this.count = count; + } + + public String getSelector() { + return selector; + } + + public void setSelector(String selector) { + this.selector = selector; + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/ContainerIdMapper.java b/modelsV2/src/main/java/info/FrostFS/sdk/mappers/ContainerIdMapper.java new file mode 100644 index 0000000..bab5b61 --- /dev/null +++ b/modelsV2/src/main/java/info/FrostFS/sdk/mappers/ContainerIdMapper.java @@ -0,0 +1,16 @@ +package info.FrostFS.sdk.mappers; + +import com.google.protobuf.ByteString; +import frostFS.refs.Types; +import info.FrostFS.sdk.jdo.ContainerId; + +public class ContainerIdMapper { + + public static Types.ContainerID toGrpcMessage(ContainerId containerId) { + var test = containerId.toHash(); + + return Types.ContainerID.newBuilder() + .setValue(ByteString.copyFrom(containerId.toHash())) + .build(); + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/ContainerMapper.java b/modelsV2/src/main/java/info/FrostFS/sdk/mappers/ContainerMapper.java new file mode 100644 index 0000000..4f1d886 --- /dev/null +++ b/modelsV2/src/main/java/info/FrostFS/sdk/mappers/ContainerMapper.java @@ -0,0 +1,39 @@ +package info.FrostFS.sdk.mappers; + + +import com.google.protobuf.ByteString; +import frostFS.container.Types; +import info.FrostFS.sdk.enums.BasicAcl; +import info.FrostFS.sdk.jdo.Container; +import info.FrostFS.sdk.mappers.netmap.PlacementPolicyMapper; + +import static info.FrostFS.sdk.UUIDExtension.asBytes; +import static info.FrostFS.sdk.UUIDExtension.asUuid; +import static java.util.Objects.isNull; + +public class ContainerMapper { + + public static Types.Container toGrpcMessage(Container container) { + return Types.Container.newBuilder() + .setBasicAcl(container.getBasicAcl().value) + .setPlacementPolicy(PlacementPolicyMapper.toGrpcMessage(container.getPlacementPolicy())) + .setNonce(ByteString.copyFrom(asBytes(container.getNonce()))) + .build(); + } + + public static Container toModel(Types.Container container) { + var basicAcl = BasicAcl.get(container.getBasicAcl()); + if (isNull(basicAcl)) { + throw new IllegalArgumentException( + String.format("Unknown BasicACL rule. Value: %s.", container.getBasicAcl()) + ); + } + + return new Container( + basicAcl, + PlacementPolicyMapper.toModel(container.getPlacementPolicy()), + asUuid(container.getNonce().toByteArray()), + VersionMapper.toModel(container.getVersion()) + ); + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/MetaHeaderMapper.java b/modelsV2/src/main/java/info/FrostFS/sdk/mappers/MetaHeaderMapper.java new file mode 100644 index 0000000..2d493c4 --- /dev/null +++ b/modelsV2/src/main/java/info/FrostFS/sdk/mappers/MetaHeaderMapper.java @@ -0,0 +1,15 @@ +package info.FrostFS.sdk.mappers; + +import frostFS.session.Types; +import info.FrostFS.sdk.jdo.MetaHeader; + +public class MetaHeaderMapper { + + public static Types.RequestMetaHeader toGrpcMessage(MetaHeader metaHeader) { + return Types.RequestMetaHeader.newBuilder() + .setVersion(VersionMapper.toGrpcMessage(metaHeader.getVersion())) + .setEpoch(metaHeader.getEpoch()) + .setTtl(metaHeader.getTtl()) + .build(); + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/ObjectAttributeMapper.java b/modelsV2/src/main/java/info/FrostFS/sdk/mappers/ObjectAttributeMapper.java new file mode 100644 index 0000000..45489a5 --- /dev/null +++ b/modelsV2/src/main/java/info/FrostFS/sdk/mappers/ObjectAttributeMapper.java @@ -0,0 +1,17 @@ +package info.FrostFS.sdk.mappers; + +import frostFS.object.Types; +import info.FrostFS.sdk.jdo.ObjectAttribute; + +public class ObjectAttributeMapper { + public static Types.Header.Attribute toGrpcMessage(ObjectAttribute attribute) { + return Types.Header.Attribute.newBuilder() + .setKey(attribute.getKey()) + .setValue(attribute.getValue()) + .build(); + } + + public static ObjectAttribute toModel(Types.Header.Attribute attribute) { + return new ObjectAttribute(attribute.getKey(), attribute.getValue()); + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/ObjectFilterMapper.java b/modelsV2/src/main/java/info/FrostFS/sdk/mappers/ObjectFilterMapper.java new file mode 100644 index 0000000..328de81 --- /dev/null +++ b/modelsV2/src/main/java/info/FrostFS/sdk/mappers/ObjectFilterMapper.java @@ -0,0 +1,25 @@ +package info.FrostFS.sdk.mappers; + +import frostFS.object.Service; +import frostFS.object.Types; +import info.FrostFS.sdk.jdo.ObjectFilter; +import org.apache.commons.lang3.EnumUtils; + +import static java.util.Objects.isNull; + +public class ObjectFilterMapper { + public static Service.SearchRequest.Body.Filter toGrpcMessage(ObjectFilter filter) { + var objectMatchType = Types.MatchType.forNumber(filter.getMatchType().value); + if (isNull(objectMatchType)) { + throw new IllegalArgumentException( + String.format("Unknown MatchType. Value: %s.", filter.getMatchType().name()) + ); + } + + return Service.SearchRequest.Body.Filter.newBuilder() + .setMatchType(objectMatchType) + .setKey(filter.getKey()) + .setValue(filter.getValue()) + .build(); + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/ObjectHeaderMapper.java b/modelsV2/src/main/java/info/FrostFS/sdk/mappers/ObjectHeaderMapper.java new file mode 100644 index 0000000..ba38b14 --- /dev/null +++ b/modelsV2/src/main/java/info/FrostFS/sdk/mappers/ObjectHeaderMapper.java @@ -0,0 +1,48 @@ +package info.FrostFS.sdk.mappers; + +import frostFS.object.Types; +import info.FrostFS.sdk.enums.ObjectType; +import info.FrostFS.sdk.jdo.ContainerId; +import info.FrostFS.sdk.jdo.ObjectAttribute; +import info.FrostFS.sdk.jdo.ObjectHeader; +import org.apache.commons.lang3.EnumUtils; + +import static java.util.Objects.isNull; + +public class ObjectHeaderMapper { + public static Types.Header toGrpcMessage(ObjectHeader header) { + var objectType = EnumUtils.getEnum(Types.ObjectType.class, header.getObjectType().name()); + if (isNull(objectType)) { + throw new IllegalArgumentException( + String.format("Unknown ObjectType. Value: %s.", header.getObjectType().name()) + ); + } + + var head = Types.Header.newBuilder() + .setContainerId(ContainerIdMapper.toGrpcMessage(header.getContainerId())) + .setObjectType(objectType); + + for (ObjectAttribute objectAttribute : header.getAttributes()) { + head.addAttributes(ObjectAttributeMapper.toGrpcMessage(objectAttribute)); + } + + return head.build(); + } + + public static ObjectHeader toModel(Types.Header header) { + var objectType = ObjectType.get(header.getObjectTypeValue()); + if (isNull(objectType)) { + throw new IllegalArgumentException( + String.format("Unknown ObjectType. Value: %s.", header.getObjectType()) + ); + } + + return new ObjectHeader( + ContainerId.fromHash(header.getContainerId().getValue().toByteArray()), + objectType, + header.getAttributesList().stream().map(ObjectAttributeMapper::toModel).toArray(ObjectAttribute[]::new), + header.getPayloadLength(), + VersionMapper.toModel(header.getVersion()) + ); + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/ObjectIdMapper.java b/modelsV2/src/main/java/info/FrostFS/sdk/mappers/ObjectIdMapper.java new file mode 100644 index 0000000..42200e2 --- /dev/null +++ b/modelsV2/src/main/java/info/FrostFS/sdk/mappers/ObjectIdMapper.java @@ -0,0 +1,13 @@ +package info.FrostFS.sdk.mappers; + +import com.google.protobuf.ByteString; +import frostFS.refs.Types; +import info.FrostFS.sdk.jdo.ObjectId; + +public class ObjectIdMapper { + public static Types.ObjectID toGrpcMessage(ObjectId objectId) { + return Types.ObjectID.newBuilder() + .setValue(ByteString.copyFrom(objectId.toHash())) + .build(); + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/ObjectMapper.java b/modelsV2/src/main/java/info/FrostFS/sdk/mappers/ObjectMapper.java new file mode 100644 index 0000000..b970456 --- /dev/null +++ b/modelsV2/src/main/java/info/FrostFS/sdk/mappers/ObjectMapper.java @@ -0,0 +1,15 @@ +package info.FrostFS.sdk.mappers; + +import frostFS.object.Types; +import info.FrostFS.sdk.jdo.ObjectFrostFs; +import info.FrostFS.sdk.jdo.ObjectId; + +public class ObjectMapper { + public static ObjectFrostFs toModel(Types.Object obj) { + return new ObjectFrostFs( + ObjectHeaderMapper.toModel(obj.getHeader()), + ObjectId.fromHash(obj.getObjectId().getValue().toByteArray()), + obj.getPayload().toByteArray() + ); + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/OwnerIdMapper.java b/modelsV2/src/main/java/info/FrostFS/sdk/mappers/OwnerIdMapper.java new file mode 100644 index 0000000..bc7027c --- /dev/null +++ b/modelsV2/src/main/java/info/FrostFS/sdk/mappers/OwnerIdMapper.java @@ -0,0 +1,13 @@ +package info.FrostFS.sdk.mappers; + +import com.google.protobuf.ByteString; +import frostFS.refs.Types; +import info.FrostFS.sdk.jdo.OwnerId; + +public class OwnerIdMapper { + public static Types.OwnerID toGrpcMessage(OwnerId ownerId) { + return Types.OwnerID.newBuilder() + .setValue(ByteString.copyFrom(ownerId.toHash())) + .build(); + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/StatusMapper.java b/modelsV2/src/main/java/info/FrostFS/sdk/mappers/StatusMapper.java new file mode 100644 index 0000000..6cb68d7 --- /dev/null +++ b/modelsV2/src/main/java/info/FrostFS/sdk/mappers/StatusMapper.java @@ -0,0 +1,23 @@ +package info.FrostFS.sdk.mappers; + +import frostFS.status.Types; +import info.FrostFS.sdk.enums.StatusCode; +import info.FrostFS.sdk.jdo.Status; + +import static java.util.Objects.isNull; + +public class StatusMapper { + public static Status toModel(Types.Status status) { + if (isNull(status)) return new Status(StatusCode.SUCCESS); + + var statusCode = StatusCode.get(status.getCode()); + if (isNull(statusCode)) { + throw new IllegalArgumentException( + String.format("Unknown StatusCode. Value: %s.", status.getCode()) + ); + + } + + return new Status(statusCode, status.getMessage()); + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/VersionMapper.java b/modelsV2/src/main/java/info/FrostFS/sdk/mappers/VersionMapper.java new file mode 100644 index 0000000..339746a --- /dev/null +++ b/modelsV2/src/main/java/info/FrostFS/sdk/mappers/VersionMapper.java @@ -0,0 +1,18 @@ +package info.FrostFS.sdk.mappers; + +import frostFS.refs.Types; +import info.FrostFS.sdk.jdo.Version; + +public class VersionMapper { + + public static Types.Version toGrpcMessage(Version version) { + return Types.Version.newBuilder() + .setMajor(version.getMajor()) + .setMinor(version.getMinor()) + .build(); + } + + public static Version toModel(Types.Version version) { + return new Version(version.getMajor(), version.getMinor()); + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/netmap/NodeInfoMapper.java b/modelsV2/src/main/java/info/FrostFS/sdk/mappers/netmap/NodeInfoMapper.java new file mode 100644 index 0000000..934d60f --- /dev/null +++ b/modelsV2/src/main/java/info/FrostFS/sdk/mappers/netmap/NodeInfoMapper.java @@ -0,0 +1,21 @@ +package info.FrostFS.sdk.mappers.netmap; + +import frostFS.netmap.Service; +import info.FrostFS.sdk.enums.NodeState; +import info.FrostFS.sdk.jdo.netmap.NodeInfo; +import info.FrostFS.sdk.mappers.VersionMapper; + +import static java.util.Objects.isNull; + +public class NodeInfoMapper { + public static NodeInfo toModel(Service.LocalNodeInfoResponse.Body nodeInfo) { + var nodeState = NodeState.get(nodeInfo.getNodeInfo().getState().getNumber()); + if (isNull(nodeState)) { + throw new IllegalArgumentException( + String.format("Unknown NodeState. Value: %s.", nodeInfo.getNodeInfo().getState()) + ); + } + + return new NodeInfo(nodeState, VersionMapper.toModel(nodeInfo.getVersion())); + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/netmap/PlacementPolicyMapper.java b/modelsV2/src/main/java/info/FrostFS/sdk/mappers/netmap/PlacementPolicyMapper.java new file mode 100644 index 0000000..871dfcb --- /dev/null +++ b/modelsV2/src/main/java/info/FrostFS/sdk/mappers/netmap/PlacementPolicyMapper.java @@ -0,0 +1,25 @@ +package info.FrostFS.sdk.mappers.netmap; + +import frostFS.netmap.Types; +import info.FrostFS.sdk.jdo.netmap.PlacementPolicy; +import info.FrostFS.sdk.jdo.netmap.Replica; + +public class PlacementPolicyMapper { + public static Types.PlacementPolicy toGrpcMessage(PlacementPolicy placementPolicy) { + var pp = Types.PlacementPolicy.newBuilder() + .setUnique(placementPolicy.isUnique()); + + for (Replica replica : placementPolicy.getReplicas()) { + pp.addReplicas(ReplicaMapper.toGrpcMessage(replica)); + } + + return pp.build(); + } + + public static PlacementPolicy toModel(Types.PlacementPolicy placementPolicy) { + return new PlacementPolicy( + placementPolicy.getUnique(), + placementPolicy.getReplicasList().stream().map(ReplicaMapper::toModel).toArray(Replica[]::new) + ); + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/netmap/ReplicaMapper.java b/modelsV2/src/main/java/info/FrostFS/sdk/mappers/netmap/ReplicaMapper.java new file mode 100644 index 0000000..93735ff --- /dev/null +++ b/modelsV2/src/main/java/info/FrostFS/sdk/mappers/netmap/ReplicaMapper.java @@ -0,0 +1,17 @@ +package info.FrostFS.sdk.mappers.netmap; + +import frostFS.netmap.Types; +import info.FrostFS.sdk.jdo.netmap.Replica; + +public class ReplicaMapper { + public static Types.Replica toGrpcMessage(Replica replica) { + return Types.Replica.newBuilder() + .setCount(replica.getCount()) + .setSelector(replica.getSelector()) + .build(); + } + + public static Replica toModel(Types.Replica replica) { + return new Replica(replica.getCount(), replica.getSelector()); + } +} diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..5c16e71 --- /dev/null +++ b/pom.xml @@ -0,0 +1,23 @@ + + + 4.0.0 + + info.FrostFS.sdk + FrostFS-sdk-java + 0.1.0 + pom + + protosV2 + client + cryptography + modelsV2 + + + + 11 + 11 + UTF-8 + + \ No newline at end of file diff --git a/protosV2/pom.xml b/protosV2/pom.xml new file mode 100644 index 0000000..d7d8630 --- /dev/null +++ b/protosV2/pom.xml @@ -0,0 +1,106 @@ + + + 4.0.0 + + info.FrostFS.sdk + FrostFS-sdk-java + 0.1.0 + + + protosV2 + + + 11 + 11 + 3.23.0 + 1.62.2 + UTF-8 + + + + + io.grpc + grpc-netty + ${grpc.version} + + + io.grpc + grpc-protobuf + ${grpc.version} + + + io.grpc + grpc-stub + ${grpc.version} + + + io.grpc + grpc-services + ${grpc.version} + runtime + + + javax.annotation + javax.annotation-api + 1.3.2 + + + com.google.protobuf + protobuf-java + ${protobuf.version} + + + + + + + io.grpc + grpc-bom + ${grpc.version} + pom + import + + + + + + + + kr.motd.maven + os-maven-plugin + 1.6.2 + + + + + org.xolstice.maven.plugins + protobuf-maven-plugin + 0.6.1 + + + com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier} + + grpc-java + + io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier} + + + + + + compile + compile-custom + + test-compile + test-compile-custom + + + + + + + + \ No newline at end of file diff --git a/protosV2/src/main/proto/accounting/service.proto b/protosV2/src/main/proto/accounting/service.proto new file mode 100644 index 0000000..c225c58 --- /dev/null +++ b/protosV2/src/main/proto/accounting/service.proto @@ -0,0 +1,71 @@ +syntax = "proto3"; + +package neo.fs.v2.accounting; + +option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/accounting/grpc;accounting"; +option java_package = "frostFS.accounting"; + +import "accounting/types.proto"; +import "refs/types.proto"; +import "session/types.proto"; + +// Accounting service provides methods for interaction with NeoFS sidechain via +// other NeoFS nodes to get information about the account balance. Deposit and +// Withdraw operations can't be implemented here, as they require Mainnet NeoFS +// smart contract invocation. Transfer operations between internal NeoFS +// accounts are possible if both use the same token type. +service AccountingService { + // Returns the amount of funds in GAS token for the requested NeoFS account. + // + // Statuses: + // - **OK** (0, SECTION_SUCCESS): + // balance has been successfully read; + // - Common failures (SECTION_FAILURE_COMMON). + rpc Balance(BalanceRequest) returns (BalanceResponse); +} + +// BalanceRequest message +message BalanceRequest { + // To indicate the account for which the balance is requested, its identifier + // is used. It can be any existing account in NeoFS sidechain `Balance` smart + // contract. If omitted, client implementation MUST set it to the request's + // signer `OwnerID`. + message Body { + // Valid user identifier in `OwnerID` format for which the balance is + // requested. Required field. + neo.fs.v2.refs.OwnerID owner_id = 1; + } + // Body of the balance request message. + Body body = 1; + + // Carries request meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.RequestMetaHeader meta_header = 2; + + // Carries request verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.RequestVerificationHeader verify_header = 3; +} + +// BalanceResponse message +message BalanceResponse { + // The amount of funds in GAS token for the `OwnerID`'s account requested. + // Balance is given in the `Decimal` format to avoid precision issues with + // rounding. + message Body { + // Amount of funds in GAS token for the requested account. + Decimal balance = 1; + } + // Body of the balance response message. + Body body = 1; + + // Carries response meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.ResponseMetaHeader meta_header = 2; + + // Carries response verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; +} diff --git a/protosV2/src/main/proto/accounting/types.proto b/protosV2/src/main/proto/accounting/types.proto new file mode 100644 index 0000000..57229d9 --- /dev/null +++ b/protosV2/src/main/proto/accounting/types.proto @@ -0,0 +1,22 @@ +syntax = "proto3"; + +package neo.fs.v2.accounting; + +option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/accounting/grpc;accounting"; +option java_package = "frostFS.accounting"; + +// Standard floating point data type can't be used in NeoFS due to inexactness +// of the result when doing lots of small number operations. To solve the lost +// precision issue, special `Decimal` format is used for monetary computations. +// +// Please see [The General Decimal Arithmetic +// Specification](http://speleotrove.com/decimal/) for detailed problem +// description. +message Decimal { + // Number in the smallest Token fractions. + int64 value = 1 [ json_name = "value" ]; + + // Precision value indicating how many smallest fractions can be in one + // integer. + uint32 precision = 2 [ json_name = "precision" ]; +} diff --git a/protosV2/src/main/proto/acl/types.proto b/protosV2/src/main/proto/acl/types.proto new file mode 100644 index 0000000..86a9311 --- /dev/null +++ b/protosV2/src/main/proto/acl/types.proto @@ -0,0 +1,227 @@ +syntax = "proto3"; + +package neo.fs.v2.acl; + +option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl/grpc;acl"; +option java_package = "frostFS.acl"; + +import "refs/types.proto"; + +// Target role of the access control rule in access control list. +enum Role { + // Unspecified role, default value + ROLE_UNSPECIFIED = 0; + + // User target rule is applied if sender is the owner of the container + USER = 1; + + // System target rule is applied if sender is a storage node within the + // container or an inner ring node + SYSTEM = 2; + + // Others target rule is applied if sender is neither a user nor a system + // target + OTHERS = 3; +} + +// MatchType is an enumeration of match types. +enum MatchType { + // Unspecified match type, default value. + MATCH_TYPE_UNSPECIFIED = 0; + + // Return true if strings are equal + STRING_EQUAL = 1; + + // Return true if strings are different + STRING_NOT_EQUAL = 2; +} + +// Request's operation type to match if the rule is applicable to a particular +// request. +enum Operation { + // Unspecified operation, default value + OPERATION_UNSPECIFIED = 0; + + // Get + GET = 1; + + // Head + HEAD = 2; + + // Put + PUT = 3; + + // Delete + DELETE = 4; + + // Search + SEARCH = 5; + + // GetRange + GETRANGE = 6; + + // GetRangeHash + GETRANGEHASH = 7; +} + +// Rule execution result action. Either allows or denies access if the rule's +// filters match. +enum Action { + // Unspecified action, default value + ACTION_UNSPECIFIED = 0; + + // Allow action + ALLOW = 1; + + // Deny action + DENY = 2; +} + +// Enumeration of possible sources of Headers to apply filters. +enum HeaderType { + // Unspecified header, default value. + HEADER_UNSPECIFIED = 0; + + // Filter request headers + REQUEST = 1; + + // Filter object headers + OBJECT = 2; + + // Filter service headers. These are not processed by NeoFS nodes and + // exist for service use only. + SERVICE = 3; +} + +// Describes a single eACL rule. +message EACLRecord { + // NeoFS request Verb to match + Operation operation = 1 [ json_name = "operation" ]; + + // Rule execution result. Either allows or denies access if filters match. + Action action = 2 [ json_name = "action" ]; + + // Filter to check particular properties of the request or the object. + // + // By default `key` field refers to the corresponding object's `Attribute`. + // Some Object's header fields can also be accessed by adding `$Object:` + // prefix to the name. Here is the list of fields available via this prefix: + // + // * $Object:version \ + // version + // * $Object:objectID \ + // object_id + // * $Object:containerID \ + // container_id + // * $Object:ownerID \ + // owner_id + // * $Object:creationEpoch \ + // creation_epoch + // * $Object:payloadLength \ + // payload_length + // * $Object:payloadHash \ + // payload_hash + // * $Object:objectType \ + // object_type + // * $Object:homomorphicHash \ + // homomorphic_hash + // + // Please note, that if request or response does not have object's headers of + // full object (Range, RangeHash, Search, Delete), it will not be possible to + // filter by object header fields or user attributes. From the well-known list + // only `$Object:objectID` and `$Object:containerID` will be available, as + // it's possible to take that information from the requested address. + message Filter { + // Define if Object or Request header will be used + HeaderType header_type = 1 [ json_name = "headerType" ]; + + // Match operation type + MatchType match_type = 2 [ json_name = "matchType" ]; + + // Name of the Header to use + string key = 3 [ json_name = "key" ]; + + // Expected Header Value or pattern to match + string value = 4 [ json_name = "value" ]; + } + + // List of filters to match and see if rule is applicable + repeated Filter filters = 3 [ json_name = "filters" ]; + + // Target to apply ACL rule. Can be a subject's role class or a list of public + // keys to match. + message Target { + // Target subject's role class + Role role = 1 [ json_name = "role" ]; + + // List of public keys to identify target subject + repeated bytes keys = 2 [ json_name = "keys" ]; + } + // List of target subjects to apply ACL rule to + repeated Target targets = 4 [ json_name = "targets" ]; +} + +// Extended ACL rules table. A list of ACL rules defined additionally to Basic +// ACL. Extended ACL rules can be attached to a container and can be updated +// or may be defined in `BearerToken` structure. Please see the corresponding +// NeoFS Technical Specification section for detailed description. +message EACLTable { + // eACL format version. Effectively, the version of API library used to create + // eACL Table. + neo.fs.v2.refs.Version version = 1 [ json_name = "version" ]; + + // Identifier of the container that should use given access control rules + neo.fs.v2.refs.ContainerID container_id = 2 [ json_name = "containerID" ]; + + // List of Extended ACL rules + repeated EACLRecord records = 3 [ json_name = "records" ]; +} + +// BearerToken allows to attach signed Extended ACL rules to the request in +// `RequestMetaHeader`. If container's Basic ACL rules allow, the attached rule +// set will be checked instead of one attached to the container itself. Just +// like [JWT](https://jwt.io), it has a limited lifetime and scope, hence can be +// used in the similar use cases, like providing authorisation to externally +// authenticated party. +// +// BearerToken can be issued only by the container's owner and must be signed +// using the key associated with the container's `OwnerID`. +message BearerToken { + // Bearer Token body structure contains Extended ACL table issued by the + // container owner with additional information preventing token abuse. + message Body { + // Table of Extended ACL rules to use instead of the ones attached to the + // container. If it contains `container_id` field, bearer token is only + // valid for this specific container. Otherwise, any container of the same + // owner is allowed. + EACLTable eacl_table = 1 [ json_name = "eaclTable" ]; + + // `OwnerID` defines to whom the token was issued. It must match the request + // originator's `OwnerID`. If empty, any token bearer will be accepted. + neo.fs.v2.refs.OwnerID owner_id = 2 [ json_name = "ownerID" ]; + + // Lifetime parameters of the token. Field names taken from + // [rfc7519](https://tools.ietf.org/html/rfc7519). + message TokenLifetime { + // Expiration Epoch + uint64 exp = 1 [ json_name = "exp" ]; + + // Not valid before Epoch + uint64 nbf = 2 [ json_name = "nbf" ]; + + // Issued at Epoch + uint64 iat = 3 [ json_name = "iat" ]; + } + // Token expiration and valid time period parameters + TokenLifetime lifetime = 3 [ json_name = "lifetime" ]; + + // AllowImpersonate flag to consider token signer as request owner. + // If this field is true extended ACL table in token body isn't processed. + bool allow_impersonate = 4 [ json_name = "allowImpersonate" ]; + } + // Bearer Token body + Body body = 1 [ json_name = "body" ]; + + // Signature of BearerToken body + neo.fs.v2.refs.Signature signature = 2 [ json_name = "signature" ]; +} diff --git a/protosV2/src/main/proto/apemanager/service.proto b/protosV2/src/main/proto/apemanager/service.proto new file mode 100644 index 0000000..da0da48 --- /dev/null +++ b/protosV2/src/main/proto/apemanager/service.proto @@ -0,0 +1,172 @@ +syntax = "proto3"; + +package frostfs.v2.apemanager; + +import "apemanager/types.proto"; +import "session/types.proto"; + +option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/apemanager/grpc;apemanager"; +option java_package = "frostFS.apemanager"; + +// `APEManagerService` provides API to manage rule chains within sidechain's +// `Policy` smart contract. +service APEManagerService { + // Add a rule chain for a specific target to `Policy` smart contract. + // + // Statuses: + // - **OK** (0, SECTION_SUCCESS): \ + // the chain has been successfully added; + // - Common failures (SECTION_FAILURE_COMMON); + // - **CONTAINER_NOT_FOUND** (3072, SECTION_CONTAINER): \ + // container (as target) not found; + // - **APE_MANAGER_ACCESS_DENIED** (5120, SECTION_APE_MANAGER): \ + // the operation is denied by the service. + rpc AddChain(AddChainRequest) returns (AddChainResponse); + + // Remove a rule chain for a specific target from `Policy` smart contract. + // RemoveChain is an idempotent operation: removal of non-existing rule chain + // also means success. + // + // Statuses: + // - **OK** (0, SECTION_SUCCESS): \ + // the chain has been successfully removed; + // - Common failures (SECTION_FAILURE_COMMON); + // - **CONTAINER_NOT_FOUND** (3072, SECTION_CONTAINER): \ + // container (as target) not found; + // - **APE_MANAGER_ACCESS_DENIED** (5120, SECTION_APE_MANAGER): \ + // the operation is denied by the service. + rpc RemoveChain(RemoveChainRequest) returns (RemoveChainResponse); + + // List chains defined for a specific target from `Policy` smart contract. + // + // Statuses: + // - **OK** (0, SECTION_SUCCESS): \ + // chains have been successfully listed; + // - Common failures (SECTION_FAILURE_COMMON); + // - **CONTAINER_NOT_FOUND** (3072, SECTION_CONTAINER): \ + // container (as target) not found; + // - **APE_MANAGER_ACCESS_DENIED** (5120, SECTION_APE_MANAGER): \ + // the operation is denied by the service. + rpc ListChains(ListChainsRequest) returns (ListChainsResponse); +} + +message AddChainRequest { + message Body { + // A target for which a rule chain is added. + ChainTarget target = 1; + + // The chain to set for the target. + Chain chain = 2; + } + + // The request's body. + Body body = 1; + + // Carries request meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.RequestMetaHeader meta_header = 2; + + // Carries request verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.RequestVerificationHeader verify_header = 3; +} + +message AddChainResponse { + message Body { + // Chain ID assigned for the added rule chain. + // If chain ID is left empty in the request, then + // it will be generated. + bytes chain_id = 1; + } + + // The response's body. + Body body = 1; + + // Carries response meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.ResponseMetaHeader meta_header = 2; + + // Carries response verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; +} + +message RemoveChainRequest { + message Body { + // Target for which a rule chain is removed. + ChainTarget target = 1; + + // Chain ID assigned for the rule chain. + bytes chain_id = 2; + } + + // The request's body. + Body body = 1; + + // Carries request meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.RequestMetaHeader meta_header = 2; + + // Carries request verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.RequestVerificationHeader verify_header = 3; +} + +message RemoveChainResponse { + // Since RemoveChain is an idempotent operation, then the only indicator that + // operation could not be performed is an error returning to a client. + message Body {} + + // The response's body. + Body body = 1; + + // Carries response meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.ResponseMetaHeader meta_header = 2; + + // Carries response verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; +} + +message ListChainsRequest { + message Body { + // Target for which rule chains are listed. + ChainTarget target = 1; + } + + // The request's body. + Body body = 1; + + // Carries request meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.RequestMetaHeader meta_header = 2; + + // Carries request verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.RequestVerificationHeader verify_header = 3; +} + +message ListChainsResponse { + message Body { + // The list of chains defined for the reqeusted target. + repeated Chain chains = 1; + } + + // The response's body. + Body body = 1; + + // Carries response meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.ResponseMetaHeader meta_header = 2; + + // Carries response verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; +} \ No newline at end of file diff --git a/protosV2/src/main/proto/apemanager/types.proto b/protosV2/src/main/proto/apemanager/types.proto new file mode 100644 index 0000000..644a8b5 --- /dev/null +++ b/protosV2/src/main/proto/apemanager/types.proto @@ -0,0 +1,34 @@ +syntax = "proto3"; + +package frostfs.v2.apemanager; + +option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/apemanager/grpc;apemanager"; +option java_package = "frostFS.apemanager"; + +// TargetType is a type target to which a rule chain is defined. +enum TargetType { + UNDEFINED = 0; + + NAMESPACE = 1; + + CONTAINER = 2; + + USER = 3; + + GROUP = 4; +} + +// ChainTarget is an object to which a rule chain is defined. +message ChainTarget { + TargetType type = 1; + + string name = 2; +} + +// Chain is a chain of rules defined for a specific target. +message Chain { + oneof kind { + // Raw representation of a serizalized rule chain. + bytes raw = 1; + } +} diff --git a/protosV2/src/main/proto/container/service.proto b/protosV2/src/main/proto/container/service.proto new file mode 100644 index 0000000..ce7634a --- /dev/null +++ b/protosV2/src/main/proto/container/service.proto @@ -0,0 +1,431 @@ +syntax = "proto3"; + +package neo.fs.v2.container; + +option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container/grpc;container"; +option java_package = "frostFS.container"; + +import "acl/types.proto"; +import "container/types.proto"; +import "refs/types.proto"; +import "session/types.proto"; + +// `ContainerService` provides API to interact with `Container` smart contract +// in NeoFS sidechain via other NeoFS nodes. All of those actions can be done +// equivalently by directly issuing transactions and RPC calls to sidechain +// nodes. +service ContainerService { + // `Put` invokes `Container` smart contract's `Put` method and returns + // response immediately. After a new block is issued in sidechain, request is + // verified by Inner Ring nodes. After one more block in sidechain, the + // container is added into smart contract storage. + // + // Statuses: + // - **OK** (0, SECTION_SUCCESS): \ + // request to save the container has been sent to the sidechain; + // - Common failures (SECTION_FAILURE_COMMON); + // - **CONTAINER_ACCESS_DENIED** (3074, SECTION_CONTAINER): \ + // container create access denied. + rpc Put(PutRequest) returns (PutResponse); + + // `Delete` invokes `Container` smart contract's `Delete` method and returns + // response immediately. After a new block is issued in sidechain, request is + // verified by Inner Ring nodes. After one more block in sidechain, the + // container is added into smart contract storage. + // + // Statuses: + // - **OK** (0, SECTION_SUCCESS): \ + // request to remove the container has been sent to the sidechain; + // - Common failures (SECTION_FAILURE_COMMON); + // - **CONTAINER_ACCESS_DENIED** (3074, SECTION_CONTAINER): \ + // container delete access denied. + rpc Delete(DeleteRequest) returns (DeleteResponse); + + // Returns container structure from `Container` smart contract storage. + // + // Statuses: + // - **OK** (0, SECTION_SUCCESS): \ + // container has been successfully read; + // - Common failures (SECTION_FAILURE_COMMON); + // - **CONTAINER_NOT_FOUND** (3072, SECTION_CONTAINER): \ + // requested container not found; + // - **CONTAINER_ACCESS_DENIED** (3074, SECTION_CONTAINER): \ + // access to container is denied. + rpc Get(GetRequest) returns (GetResponse); + + // Returns all owner's containers from 'Container` smart contract' storage. + // + // Statuses: + // - **OK** (0, SECTION_SUCCESS): \ + // container list has been successfully read; + // - Common failures (SECTION_FAILURE_COMMON); + // - **CONTAINER_ACCESS_DENIED** (3074, SECTION_CONTAINER): \ + // container list access denied. + rpc List(ListRequest) returns (ListResponse); + + // Invokes 'SetEACL' method of 'Container` smart contract and returns response + // immediately. After one more block in sidechain, changes in an Extended ACL + // are added into smart contract storage. + // + // Statuses: + // - **OK** (0, SECTION_SUCCESS): \ + // request to save container eACL has been sent to the sidechain; + // - Common failures (SECTION_FAILURE_COMMON); + // - **CONTAINER_ACCESS_DENIED** (3074, SECTION_CONTAINER): \ + // set container eACL access denied. + rpc SetExtendedACL(SetExtendedACLRequest) returns (SetExtendedACLResponse); + + // Returns Extended ACL table and signature from `Container` smart contract + // storage. + // + // Statuses: + // - **OK** (0, SECTION_SUCCESS): \ + // container eACL has been successfully read; + // - Common failures (SECTION_FAILURE_COMMON); + // - **CONTAINER_NOT_FOUND** (3072, SECTION_CONTAINER): \ + // container not found; + // - **EACL_NOT_FOUND** (3073, SECTION_CONTAINER): \ + // eACL table not found; + // - **CONTAINER_ACCESS_DENIED** (3074, SECTION_CONTAINER): \ + // access to container eACL is denied. + rpc GetExtendedACL(GetExtendedACLRequest) returns (GetExtendedACLResponse); + + // Announces the space values used by the container for P2P synchronization. + // + // Statuses: + // - **OK** (0, SECTION_SUCCESS): \ + // estimation of used space has been successfully announced; + // - Common failures (SECTION_FAILURE_COMMON). + rpc AnnounceUsedSpace(AnnounceUsedSpaceRequest) + returns (AnnounceUsedSpaceResponse); +} + +// New NeoFS Container creation request +message PutRequest { + // Container creation request has container structure's signature as a + // separate field. It's not stored in sidechain, just verified on container + // creation by `Container` smart contract. `ContainerID` is a SHA256 hash of + // the stable-marshalled container strucutre, hence there is no need for + // additional signature checks. + message Body { + // Container structure to register in NeoFS + container.Container container = 1; + + // Signature of a stable-marshalled container according to RFC-6979. + neo.fs.v2.refs.SignatureRFC6979 signature = 2; + } + // Body of container put request message. + Body body = 1; + + // Carries request meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.RequestMetaHeader meta_header = 2; + + // Carries request verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.RequestVerificationHeader verify_header = 3; +} + +// New NeoFS Container creation response +message PutResponse { + // Container put response body contains information about the newly registered + // container as seen by `Container` smart contract. `ContainerID` can be + // calculated beforehand from the container structure and compared to the one + // returned here to make sure everything has been done as expected. + message Body { + // Unique identifier of the newly created container + neo.fs.v2.refs.ContainerID container_id = 1; + } + // Body of container put response message. + Body body = 1; + + // Carries response meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.ResponseMetaHeader meta_header = 2; + + // Carries response verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; +} + +// Container removal request +message DeleteRequest { + // Container removal request body has signed `ContainerID` as a proof of + // the container owner's intent. The signature will be verified by `Container` + // smart contract, so signing algorithm must be supported by NeoVM. + message Body { + // Identifier of the container to delete from NeoFS + neo.fs.v2.refs.ContainerID container_id = 1; + + // `ContainerID` signed with the container owner's key according to + // RFC-6979. + neo.fs.v2.refs.SignatureRFC6979 signature = 2; + } + // Body of container delete request message. + Body body = 1; + + // Carries request meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.RequestMetaHeader meta_header = 2; + + // Carries request verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.RequestVerificationHeader verify_header = 3; +} + +// `DeleteResponse` has an empty body because delete operation is asynchronous +// and done via consensus in Inner Ring nodes. +message DeleteResponse { + // `DeleteResponse` has an empty body because delete operation is asynchronous + // and done via consensus in Inner Ring nodes. + message Body {} + // Body of container delete response message. + Body body = 1; + + // Carries response meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.ResponseMetaHeader meta_header = 2; + + // Carries response verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; +} + +// Get container structure +message GetRequest { + // Get container structure request body. + message Body { + // Identifier of the container to get + neo.fs.v2.refs.ContainerID container_id = 1; + } + // Body of container get request message. + Body body = 1; + + // Carries request meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.RequestMetaHeader meta_header = 2; + + // Carries request verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.RequestVerificationHeader verify_header = 3; +} + +// Get container structure +message GetResponse { + // Get container response body does not have container structure signature. It + // has been already verified upon container creation. + message Body { + // Requested container structure + Container container = 1; + + // Signature of a stable-marshalled container according to RFC-6979. + neo.fs.v2.refs.SignatureRFC6979 signature = 2; + + // Session token if the container has been created within the session + neo.fs.v2.session.SessionToken session_token = 3; + } + // Body of container get response message. + Body body = 1; + + // Carries response meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.ResponseMetaHeader meta_header = 2; + + // Carries response verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; +} + +// List containers +message ListRequest { + // List containers request body. + message Body { + // Identifier of the container owner + neo.fs.v2.refs.OwnerID owner_id = 1; + } + // Body of list containers request message + Body body = 1; + + // Carries request meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.RequestMetaHeader meta_header = 2; + + // Carries request verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.RequestVerificationHeader verify_header = 3; +} + +// List containers +message ListResponse { + // List containers response body. + message Body { + // List of `ContainerID`s belonging to the requested `OwnerID` + repeated refs.ContainerID container_ids = 1; + } + + // Body of list containers response message. + Body body = 1; + + // Carries response meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.ResponseMetaHeader meta_header = 2; + + // Carries response verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; +} + +// Set Extended ACL +message SetExtendedACLRequest { + // Set Extended ACL request body does not have separate `ContainerID` + // reference. It will be taken from `EACLTable.container_id` field. + message Body { + // Extended ACL table to set for the container + neo.fs.v2.acl.EACLTable eacl = 1; + + // Signature of stable-marshalled Extended ACL table according to RFC-6979. + neo.fs.v2.refs.SignatureRFC6979 signature = 2; + } + // Body of set extended acl request message. + Body body = 1; + + // Carries request meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.RequestMetaHeader meta_header = 2; + + // Carries request verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.RequestVerificationHeader verify_header = 3; +} + +// Set Extended ACL +message SetExtendedACLResponse { + // `SetExtendedACLResponse` has an empty body because the operation is + // asynchronous and the update should be reflected in `Container` smart + // contract's storage after next block is issued in sidechain. + message Body {} + + // Body of set extended acl response message. + Body body = 1; + + // Carries response meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.ResponseMetaHeader meta_header = 2; + + // Carries response verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; +} + +// Get Extended ACL +message GetExtendedACLRequest { + // Get Extended ACL request body + message Body { + // Identifier of the container having Extended ACL + neo.fs.v2.refs.ContainerID container_id = 1; + } + + // Body of get extended acl request message. + Body body = 1; + + // Carries request meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.RequestMetaHeader meta_header = 2; + + // Carries request verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.RequestVerificationHeader verify_header = 3; +} + +// Get Extended ACL +message GetExtendedACLResponse { + // Get Extended ACL Response body can be empty if the requested container does + // not have Extended ACL Table attached or Extended ACL has not been allowed + // at the time of container creation. + message Body { + // Extended ACL requested, if available + neo.fs.v2.acl.EACLTable eacl = 1; + + // Signature of stable-marshalled Extended ACL according to RFC-6979. + neo.fs.v2.refs.SignatureRFC6979 signature = 2; + + // Session token if Extended ACL was set within a session + neo.fs.v2.session.SessionToken session_token = 3; + } + // Body of get extended acl response message. + Body body = 1; + + // Carries response meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.ResponseMetaHeader meta_header = 2; + + // Carries response verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; +} + +// Announce container used space +message AnnounceUsedSpaceRequest { + // Container used space announcement body. + message Body { + // Announcement contains used space information for a single container. + message Announcement { + // Epoch number for which the container size estimation was produced. + uint64 epoch = 1; + + // Identifier of the container. + neo.fs.v2.refs.ContainerID container_id = 2; + + // Used space is a sum of object payload sizes of a specified + // container, stored in the node. It must not include inhumed objects. + uint64 used_space = 3; + } + + // List of announcements. If nodes share several containers, + // announcements are transferred in a batch. + repeated Announcement announcements = 1; + } + + // Body of announce used space request message. + Body body = 1; + + // Carries request meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.RequestMetaHeader meta_header = 2; + + // Carries request verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.RequestVerificationHeader verify_header = 3; +} + +// Announce container used space +message AnnounceUsedSpaceResponse { + // `AnnounceUsedSpaceResponse` has an empty body because announcements are + // one way communication. + message Body {} + + // Body of announce used space response message. + Body body = 1; + + // Carries response meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.ResponseMetaHeader meta_header = 2; + + // Carries response verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; +} diff --git a/protosV2/src/main/proto/container/types.proto b/protosV2/src/main/proto/container/types.proto new file mode 100644 index 0000000..128a0bd --- /dev/null +++ b/protosV2/src/main/proto/container/types.proto @@ -0,0 +1,76 @@ +syntax = "proto3"; + +package neo.fs.v2.container; + +option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container/grpc;container"; +option java_package = "frostFS.container"; + +import "netmap/types.proto"; +import "refs/types.proto"; + +// Container is a structure that defines object placement behaviour. Objects can +// be stored only within containers. They define placement rule, attributes and +// access control information. An ID of a container is a 32 byte long SHA256 +// hash of stable-marshalled container message. +message Container { + // Container format version. Effectively, the version of API library used to + // create the container. + neo.fs.v2.refs.Version version = 1 [ json_name = "version" ]; + + // Identifier of the container owner + neo.fs.v2.refs.OwnerID owner_id = 2 [ json_name = "ownerID" ]; + + // Nonce is a 16 byte UUIDv4, used to avoid collisions of `ContainerID`s + bytes nonce = 3 [ json_name = "nonce" ]; + + // `BasicACL` contains access control rules for the owner, system and others + // groups, as well as permission bits for `BearerToken` and `Extended ACL` + uint32 basic_acl = 4 [ json_name = "basicACL" ]; + + // `Attribute` is a user-defined Key-Value metadata pair attached to the + // container. Container attributes are immutable. They are set at the moment + // of container creation and can never be added or updated. + // + // Key name must be a container-unique valid UTF-8 string. Value can't be + // empty. Containers with duplicated attribute names or attributes with empty + // values will be considered invalid. + // + // There are some "well-known" attributes affecting system behaviour: + // + // * [ __SYSTEM__NAME ] \ + // (`__NEOFS__NAME` is deprecated) \ + // String of a human-friendly container name registered as a domain in + // NNS contract. + // * [ __SYSTEM__ZONE ] \ + // (`__NEOFS__ZONE` is deprecated) \ + // String of a zone for `__SYSTEM__NAME` (`__NEOFS__NAME` is deprecated). + // Used as a TLD of a domain name in NNS contract. If no zone is specified, + // use default zone: `container`. + // * [ __SYSTEM__DISABLE_HOMOMORPHIC_HASHING ] \ + // (`__NEOFS__DISABLE_HOMOMORPHIC_HASHING` is deprecated) \ + // Disables homomorphic hashing for the container if the value equals "true" + // string. Any other values are interpreted as missing attribute. Container + // could be accepted in a NeoFS network only if the global network hashing + // configuration value corresponds with that attribute's value. After + // container inclusion, network setting is ignored. + // + // And some well-known attributes used by applications only: + // + // * Name \ + // Human-friendly name + // * Timestamp \ + // User-defined local time of container creation in Unix Timestamp format + message Attribute { + // Attribute name key + string key = 1 [ json_name = "key" ]; + + // Attribute value + string value = 2 [ json_name = "value" ]; + } + // Attributes represent immutable container's meta data + repeated Attribute attributes = 5 [ json_name = "attributes" ]; + + // Placement policy for the object inside the container + neo.fs.v2.netmap.PlacementPolicy placement_policy = 6 + [ json_name = "placementPolicy" ]; +} diff --git a/protosV2/src/main/proto/lock/types.proto b/protosV2/src/main/proto/lock/types.proto new file mode 100644 index 0000000..85766db --- /dev/null +++ b/protosV2/src/main/proto/lock/types.proto @@ -0,0 +1,19 @@ +syntax = "proto3"; + +package neo.fs.v2.lock; + +option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/lock/grpc;lock"; +option java_package = "frostFS.lock"; + +import "refs/types.proto"; + +// Lock objects protects a list of objects from being deleted. The lifetime of a +// lock object is limited similar to regular objects in +// `__SYSTEM__EXPIRATION_EPOCH` (`__NEOFS__EXPIRATION_EPOCH` is deprecated) +// attribute. Lock object MUST have expiration epoch. It is impossible to delete +// a lock object via ObjectService.Delete RPC call. +message Lock { + // List of objects to lock. Must not be empty or carry empty IDs. + // All members must be of the `REGULAR` type. + repeated neo.fs.v2.refs.ObjectID members = 1 [ json_name = "members" ]; +} diff --git a/protosV2/src/main/proto/netmap/service.proto b/protosV2/src/main/proto/netmap/service.proto new file mode 100644 index 0000000..ac4f4cc --- /dev/null +++ b/protosV2/src/main/proto/netmap/service.proto @@ -0,0 +1,162 @@ +syntax = "proto3"; + +package neo.fs.v2.netmap; + +option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap/grpc;netmap"; +option java_package = "frostFS.netmap"; + +import "netmap/types.proto"; +import "refs/types.proto"; +import "session/types.proto"; + +// `NetmapService` provides methods to work with `Network Map` and the +// information required to build it. The resulting `Network Map` is stored in +// sidechain `Netmap` smart contract, while related information can be obtained +// from other NeoFS nodes. +service NetmapService { + // Get NodeInfo structure from the particular node directly. + // Node information can be taken from `Netmap` smart contract. In some cases, + // though, one may want to get recent information directly or to talk to the + // node not yet present in the `Network Map` to find out what API version can + // be used for further communication. This can be also used to check if a node + // is up and running. + // + // Statuses: + // - **OK** (0, SECTION_SUCCESS): + // information about the server has been successfully read; + // - Common failures (SECTION_FAILURE_COMMON). + rpc LocalNodeInfo(LocalNodeInfoRequest) returns (LocalNodeInfoResponse); + + // Read recent information about the NeoFS network. + // + // Statuses: + // - **OK** (0, SECTION_SUCCESS): + // information about the current network state has been successfully read; + // - Common failures (SECTION_FAILURE_COMMON). + rpc NetworkInfo(NetworkInfoRequest) returns (NetworkInfoResponse); + + // Returns network map snapshot of the current NeoFS epoch. + // + // Statuses: + // - **OK** (0, SECTION_SUCCESS): + // information about the current network map has been successfully read; + // - Common failures (SECTION_FAILURE_COMMON). + rpc NetmapSnapshot(NetmapSnapshotRequest) returns (NetmapSnapshotResponse); +} + +// Get NodeInfo structure directly from a particular node +message LocalNodeInfoRequest { + // LocalNodeInfo request body is empty. + message Body {} + // Body of the LocalNodeInfo request message + Body body = 1; + + // Carries request meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.RequestMetaHeader meta_header = 2; + + // Carries request verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.RequestVerificationHeader verify_header = 3; +} + +// Local Node Info, including API Version in use +message LocalNodeInfoResponse { + // Local Node Info, including API Version in use. + message Body { + // Latest NeoFS API version in use + neo.fs.v2.refs.Version version = 1; + + // NodeInfo structure with recent information from node itself + NodeInfo node_info = 2; + } + // Body of the balance response message. + Body body = 1; + + // Carries response meta information. Header data is used only to regulate + // message transport and does not affect response execution. + neo.fs.v2.session.ResponseMetaHeader meta_header = 2; + + // Carries response verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; +} + +// Get NetworkInfo structure with the network view from a particular node. +message NetworkInfoRequest { + // NetworkInfo request body is empty. + message Body {} + // Body of the NetworkInfo request message + Body body = 1; + + // Carries request meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.RequestMetaHeader meta_header = 2; + + // Carries request verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.RequestVerificationHeader verify_header = 3; +} + +// Response with NetworkInfo structure including current epoch and +// sidechain magic number. +message NetworkInfoResponse { + // Information about the network. + message Body { + // NetworkInfo structure with recent information. + NetworkInfo network_info = 1; + } + // Body of the NetworkInfo response message. + Body body = 1; + + // Carries response meta information. Header data is used only to regulate + // message transport and does not affect response execution. + neo.fs.v2.session.ResponseMetaHeader meta_header = 2; + + // Carries response verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; +} + +// Get netmap snapshot request +message NetmapSnapshotRequest { + // Get netmap snapshot request body. + message Body {} + + // Body of get netmap snapshot request message. + Body body = 1; + + // Carries request meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.RequestMetaHeader meta_header = 2; + + // Carries request verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.RequestVerificationHeader verify_header = 3; +} + +// Response with current netmap snapshot +message NetmapSnapshotResponse { + // Get netmap snapshot response body + message Body { + // Structure of the requested network map. + Netmap netmap = 1 [ json_name = "netmap" ]; + } + + // Body of get netmap snapshot response message. + Body body = 1; + + // Carries response meta information. Header data is used only to regulate + // message transport and does not affect response execution. + neo.fs.v2.session.ResponseMetaHeader meta_header = 2; + + // Carries response verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; +} diff --git a/protosV2/src/main/proto/netmap/types.proto b/protosV2/src/main/proto/netmap/types.proto new file mode 100644 index 0000000..1ff2e9e --- /dev/null +++ b/protosV2/src/main/proto/netmap/types.proto @@ -0,0 +1,323 @@ +syntax = "proto3"; + +package neo.fs.v2.netmap; + +option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap/grpc;netmap"; +option java_package = "frostFS.netmap"; + +// Operations on filters +enum Operation { + // No Operation defined + OPERATION_UNSPECIFIED = 0; + + // Equal + EQ = 1; + + // Not Equal + NE = 2; + + // Greater then + GT = 3; + + // Greater or equal + GE = 4; + + // Less then + LT = 5; + + // Less or equal + LE = 6; + + // Logical OR + OR = 7; + + // Logical AND + AND = 8; + + // Logical negation + NOT = 9; +} + +// Selector modifier shows how the node set will be formed. By default selector +// just groups nodes into a bucket by attribute, selecting nodes only by their +// hash distance. +enum Clause { + // No modifier defined. Nodes will be selected from the bucket randomly + CLAUSE_UNSPECIFIED = 0; + + // SAME will select only nodes having the same value of bucket attribute + SAME = 1; + + // DISTINCT will select nodes having different values of bucket attribute + DISTINCT = 2; +} + +// This filter will return the subset of nodes from `NetworkMap` or another +// filter's results that will satisfy filter's conditions. +message Filter { + // Name of the filter or a reference to a named filter. '*' means + // application to the whole unfiltered NetworkMap. At top level it's used as a + // filter name. At lower levels it's considered to be a reference to another + // named filter + string name = 1 [ json_name = "name" ]; + + // Key to filter + string key = 2 [ json_name = "key" ]; + + // Filtering operation + Operation op = 3 [ json_name = "op" ]; + + // Value to match + string value = 4 [ json_name = "value" ]; + + // List of inner filters. Top level operation will be applied to the whole + // list. + repeated Filter filters = 5 [ json_name = "filters" ]; +} + +// Selector chooses a number of nodes from the bucket taking the nearest nodes +// to the provided `ContainerID` by hash distance. +message Selector { + // Selector name to reference in object placement section + string name = 1 [ json_name = "name" ]; + + // How many nodes to select from the bucket + uint32 count = 2 [ json_name = "count" ]; + + // Selector modifier showing how to form a bucket + Clause clause = 3 [ json_name = "clause" ]; + + // Bucket attribute to select from + string attribute = 4 [ json_name = "attribute" ]; + + // Filter reference to select from + string filter = 5 [ json_name = "filter" ]; +} + +// Number of object replicas in a set of nodes from the defined selector. If no +// selector set, the root bucket containing all possible nodes will be used by +// default. +message Replica { + // How many object replicas to put + uint32 count = 1 [ json_name = "count" ]; + + // Named selector bucket to put replicas + string selector = 2 [ json_name = "selector" ]; + + // Data shards count + uint32 ec_data_count = 3 [ json_name = "ecDataCount" ]; + + // Parity shards count + uint32 ec_parity_count = 4 [ json_name = "ecParityCount" ]; +} + +// Set of rules to select a subset of nodes from `NetworkMap` able to store +// container's objects. The format is simple enough to transpile from different +// storage policy definition languages. +message PlacementPolicy { + // Rules to set number of object replicas and place each one into a named + // bucket + repeated Replica replicas = 1 [ json_name = "replicas" ]; + + // Container backup factor controls how deep NeoFS will search for nodes + // alternatives to include into container's nodes subset + uint32 container_backup_factor = 2 [ json_name = "containerBackupFactor" ]; + + // Set of Selectors to form the container's nodes subset + repeated Selector selectors = 3 [ json_name = "selectors" ]; + + // List of named filters to reference in selectors + repeated Filter filters = 4 [ json_name = "filters" ]; + + // Unique flag defines non-overlapping application for replicas + bool unique = 5 [ json_name = "unique" ]; +} + +// NeoFS node description +message NodeInfo { + // Public key of the NeoFS node in a binary format + bytes public_key = 1 [ json_name = "publicKey" ]; + + // Ways to connect to a node + repeated string addresses = 2 [ json_name = "addresses" ]; + + // Administrator-defined Attributes of the NeoFS Storage Node. + // + // `Attribute` is a Key-Value metadata pair. Key name must be a valid UTF-8 + // string. Value can't be empty. + // + // Attributes can be constructed into a chain of attributes: any attribute can + // have a parent attribute and a child attribute (except the first and the + // last one). A string representation of the chain of attributes in NeoFS + // Storage Node configuration uses ":" and "/" symbols, e.g.: + // + // `NEOFS_NODE_ATTRIBUTE_1=key1:val1/key2:val2` + // + // Therefore the string attribute representation in the Node configuration + // must use "\:", "\/" and "\\" escaped symbols if any of them appears in an + // attribute's key or value. + // + // Node's attributes are mostly used during Storage Policy evaluation to + // calculate object's placement and find a set of nodes satisfying policy + // requirements. There are some "well-known" node attributes common to all the + // Storage Nodes in the network and used implicitly with default values if not + // explicitly set: + // + // * Capacity \ + // Total available disk space in Gigabytes. + // * Price \ + // Price in GAS tokens for storing one GB of data during one Epoch. In node + // attributes it's a string presenting floating point number with comma or + // point delimiter for decimal part. In the Network Map it will be saved as + // 64-bit unsigned integer representing number of minimal token fractions. + // * UN-LOCODE \ + // Node's geographic location in + // [UN/LOCODE](https://www.unece.org/cefact/codesfortrade/codes_index.html) + // format approximated to the nearest point defined in the standard. + // * CountryCode \ + // Country code in + // [ISO 3166-1_alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) + // format. Calculated automatically from `UN-LOCODE` attribute. + // * Country \ + // Country short name in English, as defined in + // [ISO-3166](https://www.iso.org/obp/ui/#search). Calculated automatically + // from `UN-LOCODE` attribute. + // * Location \ + // Place names are given, whenever possible, in their national language + // versions as expressed in the Roman alphabet using the 26 characters of + // the character set adopted for international trade data interchange, + // written without diacritics . Calculated automatically from `UN-LOCODE` + // attribute. + // * SubDivCode \ + // Country's administrative subdivision where node is located. Calculated + // automatically from `UN-LOCODE` attribute based on `SubDiv` field. + // Presented in [ISO 3166-2](https://en.wikipedia.org/wiki/ISO_3166-2) + // format. + // * SubDiv \ + // Country's administrative subdivision name, as defined in + // [ISO 3166-2](https://en.wikipedia.org/wiki/ISO_3166-2). Calculated + // automatically from `UN-LOCODE` attribute. + // * Continent \ + // Node's continent name according to the [Seven-Continent model] + // (https://en.wikipedia.org/wiki/Continent#Number). Calculated + // automatically from `UN-LOCODE` attribute. + // * ExternalAddr + // Node's preferred way for communications with external clients. + // Clients SHOULD use these addresses if possible. + // Must contain a comma-separated list of multi-addresses. + // + // For detailed description of each well-known attribute please see the + // corresponding section in NeoFS Technical Specification. + message Attribute { + // Key of the node attribute + string key = 1 [ json_name = "key" ]; + + // Value of the node attribute + string value = 2 [ json_name = "value" ]; + + // Parent keys, if any. For example for `City` it could be `Region` and + // `Country`. + repeated string parents = 3 [ json_name = "parents" ]; + } + // Carries list of the NeoFS node attributes in a key-value form. Key name + // must be a node-unique valid UTF-8 string. Value can't be empty. NodeInfo + // structures with duplicated attribute names or attributes with empty values + // will be considered invalid. + repeated Attribute attributes = 3 [ json_name = "attributes" ]; + + // Represents the enumeration of various states of the NeoFS node. + enum State { + // Unknown state + UNSPECIFIED = 0; + + // Active state in the network + ONLINE = 1; + + // Network unavailable state + OFFLINE = 2; + + // Maintenance state + MAINTENANCE = 3; + } + + // Carries state of the NeoFS node + State state = 4 [ json_name = "state" ]; +} + +// Network map structure +message Netmap { + // Network map revision number. + uint64 epoch = 1 [ json_name = "epoch" ]; + + // Nodes presented in network. + repeated NodeInfo nodes = 2 [ json_name = "nodes" ]; +} + +// NeoFS network configuration +message NetworkConfig { + // Single configuration parameter. Key MUST be network-unique. + // + // System parameters: + // - **AuditFee** \ + // Fee paid by the storage group owner to the Inner Ring member. + // Value: little-endian integer. Default: 0. + // - **BasicIncomeRate** \ + // Cost of storing one gigabyte of data for a period of one epoch. Paid by + // container owner to container nodes. + // Value: little-endian integer. Default: 0. + // - **ContainerAliasFee** \ + // Fee paid for named container's creation by the container owner. + // Value: little-endian integer. Default: 0. + // - **ContainerFee** \ + // Fee paid for container creation by the container owner. + // Value: little-endian integer. Default: 0. + // - **EpochDuration** \ + // NeoFS epoch duration measured in Sidechain blocks. + // Value: little-endian integer. Default: 0. + // - **HomomorphicHashingDisabled** \ + // Flag of disabling the homomorphic hashing of objects' payload. + // Value: true if any byte != 0. Default: false. + // - **InnerRingCandidateFee** \ + // Fee for entrance to the Inner Ring paid by the candidate. + // Value: little-endian integer. Default: 0. + // - **MaintenanceModeAllowed** \ + // Flag allowing setting the MAINTENANCE state to storage nodes. + // Value: true if any byte != 0. Default: false. + // - **MaxObjectSize** \ + // Maximum size of physically stored NeoFS object measured in bytes. + // Value: little-endian integer. Default: 0. + // - **WithdrawFee** \ + // Fee paid for withdrawal of funds paid by the account owner. + // Value: little-endian integer. Default: 0. + // - **MaxECDataCount** \ + // Maximum number of data shards for EC placement policy. + // Value: little-endian integer. Default: 0. + // - **MaxECParityCount** \ + // Maximum number of parity shards for EC placement policy. + // Value: little-endian integer. Default: 0. + message Parameter { + // Parameter key. UTF-8 encoded string + bytes key = 1 [ json_name = "key" ]; + + // Parameter value + bytes value = 2 [ json_name = "value" ]; + } + // List of parameter values + repeated Parameter parameters = 1 [ json_name = "parameters" ]; +} + +// Information about NeoFS network +message NetworkInfo { + // Number of the current epoch in the NeoFS network + uint64 current_epoch = 1 [ json_name = "currentEpoch" ]; + + // Magic number of the sidechain of the NeoFS network + uint64 magic_number = 2 [ json_name = "magicNumber" ]; + + // MillisecondsPerBlock network parameter of the sidechain of the NeoFS + // network + int64 ms_per_block = 3 [ json_name = "msPerBlock" ]; + + // NeoFS network configuration + NetworkConfig network_config = 4 [ json_name = "networkConfig" ]; +} diff --git a/protosV2/src/main/proto/object/service.proto b/protosV2/src/main/proto/object/service.proto new file mode 100644 index 0000000..98ce6f0 --- /dev/null +++ b/protosV2/src/main/proto/object/service.proto @@ -0,0 +1,816 @@ +syntax = "proto3"; + +package neo.fs.v2.object; + +option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object/grpc;object"; +option java_package = "frostFS.object"; + +import "object/types.proto"; +import "refs/types.proto"; +import "session/types.proto"; + +// `ObjectService` provides API for manipulating objects. Object operations do +// not affect the sidechain and are only served by nodes in p2p style. +service ObjectService { + // Receive full object structure, including Headers and payload. Response uses + // gRPC stream. First response message carries the object with the requested + // address. Chunk messages are parts of the object's payload if it is needed. + // All messages, except the first one, carry payload chunks. The requested + // object can be restored by concatenation of object message payload and all + // chunks keeping the receiving order. + // + // Extended headers can change `Get` behaviour: + // * [ __SYSTEM__NETMAP_EPOCH ] \ + // (`__NEOFS__NETMAP_EPOCH` is deprecated) \ + // Will use the requsted version of Network Map for object placement + // calculation. + // * [ __SYSTEM__NETMAP_LOOKUP_DEPTH ] \ + // (`__NEOFS__NETMAP_LOOKUP_DEPTH` is deprecated) \ + // Will try older versions (starting from `__SYSTEM__NETMAP_EPOCH` + // (`__NEOFS__NETMAP_EPOCH` is deprecated) if specified or the latest one + // otherwise) of Network Map to find an object until the depth limit is + // reached. + // + // Please refer to detailed `XHeader` description. + // + // Statuses: + // - **OK** (0, SECTION_SUCCESS): \ + // object has been successfully read; + // - Common failures (SECTION_FAILURE_COMMON); + // - **ACCESS_DENIED** (2048, SECTION_OBJECT): \ + // read access to the object is denied; + // - **OBJECT_NOT_FOUND** (2049, SECTION_OBJECT): \ + // object not found in container; + // - **OBJECT_ALREADY_REMOVED** (2052, SECTION_OBJECT): \ + // the requested object has been marked as deleted; + // - **CONTAINER_NOT_FOUND** (3072, SECTION_CONTAINER): \ + // object container not found; + // - **CONTAINER_ACCESS_DENIED** (3074, SECTION_CONTAINER): \ + // access to container is denied; + // - **TOKEN_EXPIRED** (4097, SECTION_SESSION): \ + // provided session token has expired. + rpc Get(GetRequest) returns (stream GetResponse); + + // Put the object into container. Request uses gRPC stream. First message + // SHOULD be of PutHeader type. `ContainerID` and `OwnerID` of an object + // SHOULD be set. Session token SHOULD be obtained before `PUT` operation (see + // session package). Chunk messages are considered by server as a part of an + // object payload. All messages, except first one, SHOULD be payload chunks. + // Chunk messages SHOULD be sent in the direct order of fragmentation. + // + // Extended headers can change `Put` behaviour: + // * [ __SYSTEM__NETMAP_EPOCH \ + // (`__NEOFS__NETMAP_EPOCH` is deprecated) \ + // Will use the requsted version of Network Map for object placement + // calculation. + // + // Please refer to detailed `XHeader` description. + // + // Statuses: + // - **OK** (0, SECTION_SUCCESS): \ + // object has been successfully saved in the container; + // - Common failures (SECTION_FAILURE_COMMON); + // - **ACCESS_DENIED** (2048, SECTION_OBJECT): \ + // write access to the container is denied; + // - **LOCKED** (2050, SECTION_OBJECT): \ + // placement of an object of type TOMBSTONE that includes at least one + // locked object is prohibited; + // - **LOCK_NON_REGULAR_OBJECT** (2051, SECTION_OBJECT): \ + // placement of an object of type LOCK that includes at least one object of + // type other than REGULAR is prohibited; + // - **CONTAINER_NOT_FOUND** (3072, SECTION_CONTAINER): \ + // object storage container not found; + // - **CONTAINER_ACCESS_DENIED** (3074, SECTION_CONTAINER): \ + // access to container is denied; + // - **TOKEN_NOT_FOUND** (4096, SECTION_SESSION): \ + // (for trusted object preparation) session private key does not exist or + // has + // been deleted; + // - **TOKEN_EXPIRED** (4097, SECTION_SESSION): \ + // provided session token has expired. + rpc Put(stream PutRequest) returns (PutResponse); + + // Delete the object from a container. There is no immediate removal + // guarantee. Object will be marked for removal and deleted eventually. + // + // Extended headers can change `Delete` behaviour: + // * [ __SYSTEM__NETMAP_EPOCH ] \ + // (`__NEOFS__NETMAP_EPOCH` is deprecated) \ + // Will use the requested version of Network Map for object placement + // calculation. + // + // Please refer to detailed `XHeader` description. + // + // Statuses: + // - **OK** (0, SECTION_SUCCESS): \ + // object has been successfully marked to be removed from the container; + // - Common failures (SECTION_FAILURE_COMMON); + // - **ACCESS_DENIED** (2048, SECTION_OBJECT): \ + // delete access to the object is denied; + // - **OBJECT_NOT_FOUND** (2049, SECTION_OBJECT): \ + // the object could not be deleted because it has not been \ + // found within the container; + // - **LOCKED** (2050, SECTION_OBJECT): \ + // deleting a locked object is prohibited; + // - **CONTAINER_NOT_FOUND** (3072, SECTION_CONTAINER): \ + // object container not found; + // - **CONTAINER_ACCESS_DENIED** (3074, SECTION_CONTAINER): \ + // access to container is denied; + // - **TOKEN_EXPIRED** (4097, SECTION_SESSION): \ + // provided session token has expired. + rpc Delete(DeleteRequest) returns (DeleteResponse); + + // Returns the object Headers without data payload. By default full header is + // returned. If `main_only` request field is set, the short header with only + // the very minimal information will be returned instead. + // + // Extended headers can change `Head` behaviour: + // * [ __SYSTEM__NETMAP_EPOCH ] \ + // (`__NEOFS__NETMAP_EPOCH` is deprecated) \ + // Will use the requested version of Network Map for object placement + // calculation. + // + // Please refer to detailed `XHeader` description. + // + // Statuses: + // - **OK** (0, SECTION_SUCCESS): \ + // object header has been successfully read; + // - Common failures (SECTION_FAILURE_COMMON); + // - **ACCESS_DENIED** (2048, SECTION_OBJECT): \ + // access to operation HEAD of the object is denied; + // - **OBJECT_NOT_FOUND** (2049, SECTION_OBJECT): \ + // object not found in container; + // - **OBJECT_ALREADY_REMOVED** (2052, SECTION_OBJECT): \ + // the requested object has been marked as deleted; + // - **CONTAINER_NOT_FOUND** (3072, SECTION_CONTAINER): \ + // object container not found; + // - **CONTAINER_ACCESS_DENIED** (3074, SECTION_CONTAINER): \ + // access to container is denied; + // - **TOKEN_EXPIRED** (4097, SECTION_SESSION): \ + // provided session token has expired. + rpc Head(HeadRequest) returns (HeadResponse); + + // Search objects in container. Search query allows to match by Object + // Header's filed values. Please see the corresponding NeoFS Technical + // Specification section for more details. + // + // Extended headers can change `Search` behaviour: + // * [ __SYSTEM__NETMAP_EPOCH ] \ + // (`__NEOFS__NETMAP_EPOCH` is deprecated) \ + // Will use the requested version of Network Map for object placement + // calculation. + // + // Please refer to detailed `XHeader` description. + // + // Statuses: + // - **OK** (0, SECTION_SUCCESS): \ + // objects have been successfully selected; + // - Common failures (SECTION_FAILURE_COMMON); + // - **ACCESS_DENIED** (2048, SECTION_OBJECT): \ + // access to operation SEARCH of the object is denied; + // - **CONTAINER_NOT_FOUND** (3072, SECTION_CONTAINER): \ + // search container not found; + // - **CONTAINER_ACCESS_DENIED** (3074, SECTION_CONTAINER): \ + // access to container is denied; + // - **TOKEN_EXPIRED** (4097, SECTION_SESSION): \ + // provided session token has expired. + rpc Search(SearchRequest) returns (stream SearchResponse); + + // Get byte range of data payload. Range is set as an (offset, length) tuple. + // Like in `Get` method, the response uses gRPC stream. Requested range can be + // restored by concatenation of all received payload chunks keeping the + // receiving order. + // + // Extended headers can change `GetRange` behaviour: + // * [ __SYSTEM__NETMAP_EPOCH ] \ + // (`__NEOFS__NETMAP_EPOCH` is deprecated) \ + // Will use the requested version of Network Map for object placement + // calculation. + // * [ __SYSTEM__NETMAP_LOOKUP_DEPTH ] \ + // (`__NEOFS__NETMAP_LOOKUP_DEPTH` is deprecated) \ + // Will try older versions of Network Map to find an object until the depth + // limit is reached. + // + // Please refer to detailed `XHeader` description. + // + // Statuses: + // - **OK** (0, SECTION_SUCCESS): \ + // data range of the object payload has been successfully read; + // - Common failures (SECTION_FAILURE_COMMON); + // - **ACCESS_DENIED** (2048, SECTION_OBJECT): \ + // access to operation RANGE of the object is denied; + // - **OBJECT_NOT_FOUND** (2049, SECTION_OBJECT): \ + // object not found in container; + // - **OBJECT_ALREADY_REMOVED** (2052, SECTION_OBJECT): \ + // the requested object has been marked as deleted. + // - **OUT_OF_RANGE** (2053, SECTION_OBJECT): \ + // the requested range is out of bounds; + // - **CONTAINER_NOT_FOUND** (3072, SECTION_CONTAINER): \ + // object container not found; + // - **CONTAINER_ACCESS_DENIED** (3074, SECTION_CONTAINER): \ + // access to container is denied; + // - **TOKEN_EXPIRED** (4097, SECTION_SESSION): \ + // provided session token has expired. + rpc GetRange(GetRangeRequest) returns (stream GetRangeResponse); + + // Returns homomorphic or regular hash of object's payload range after + // applying XOR operation with the provided `salt`. Ranges are set of (offset, + // length) tuples. Hashes order in response corresponds to the ranges order in + // the request. Note that hash is calculated for XORed data. + // + // Extended headers can change `GetRangeHash` behaviour: + // * [ __SYSTEM__NETMAP_EPOCH ] \ + // (`__NEOFS__NETMAP_EPOCH` is deprecated) \ + // Will use the requested version of Network Map for object placement + // calculation. + // * [ __SYSTEM__NETMAP_LOOKUP_DEPTH ] \ + // (`__NEOFS__NETMAP_LOOKUP_DEPTH` is deprecated) \ + // Will try older versions of Network Map to find an object until the depth + // limit is reached. + // + // Please refer to detailed `XHeader` description. + // + // Statuses: + // - **OK** (0, SECTION_SUCCESS): \ + // data range of the object payload has been successfully hashed; + // - Common failures (SECTION_FAILURE_COMMON); + // - **ACCESS_DENIED** (2048, SECTION_OBJECT): \ + // access to operation RANGEHASH of the object is denied; + // - **OBJECT_NOT_FOUND** (2049, SECTION_OBJECT): \ + // object not found in container; + // - **OUT_OF_RANGE** (2053, SECTION_OBJECT): \ + // the requested range is out of bounds; + // - **CONTAINER_NOT_FOUND** (3072, SECTION_CONTAINER): \ + // object container not found; + // - **CONTAINER_ACCESS_DENIED** (3074, SECTION_CONTAINER): \ + // access to container is denied; + // - **TOKEN_EXPIRED** (4097, SECTION_SESSION): \ + // provided session token has expired. + rpc GetRangeHash(GetRangeHashRequest) returns (GetRangeHashResponse); + + // Put the prepared object into container. + // `ContainerID`, `ObjectID`, `OwnerID`, `PayloadHash` and `PayloadLength` of + // an object MUST be set. + // + // Extended headers can change `Put` behaviour: + // * [ __SYSTEM__NETMAP_EPOCH \ + // (`__NEOFS__NETMAP_EPOCH` is deprecated) \ + // Will use the requested version of Network Map for object placement + // calculation. + // + // Please refer to detailed `XHeader` description. + // + // Statuses: + // - **OK** (0, SECTION_SUCCESS): \ + // object has been successfully saved in the container; + // - Common failures (SECTION_FAILURE_COMMON); + // - **ACCESS_DENIED** (2048, SECTION_OBJECT): \ + // write access to the container is denied; + // - **LOCKED** (2050, SECTION_OBJECT): \ + // placement of an object of type TOMBSTONE that includes at least one + // locked object is prohibited; + // - **LOCK_NON_REGULAR_OBJECT** (2051, SECTION_OBJECT): \ + // placement of an object of type LOCK that includes at least one object of + // type other than REGULAR is prohibited; + // - **CONTAINER_NOT_FOUND** (3072, SECTION_CONTAINER): \ + // object storage container not found; + // - **CONTAINER_ACCESS_DENIED** (3074, SECTION_CONTAINER): \ + // access to container is denied; + // - **TOKEN_NOT_FOUND** (4096, SECTION_SESSION): \ + // (for trusted object preparation) session private key does not exist or + // has + // been deleted; + // - **TOKEN_EXPIRED** (4097, SECTION_SESSION): \ + // provided session token has expired. + rpc PutSingle(PutSingleRequest) returns (PutSingleResponse); +} + +// GET object request +message GetRequest { + // GET Object request body + message Body { + // Address of the requested object + neo.fs.v2.refs.Address address = 1; + + // If `raw` flag is set, request will work only with objects that are + // physically stored on the peer node + bool raw = 2; + } + // Body of get object request message. + Body body = 1; + + // Carries request meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.RequestMetaHeader meta_header = 2; + + // Carries request verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.RequestVerificationHeader verify_header = 3; +} + +// GET object response +message GetResponse { + // GET Object Response body + message Body { + // Initial part of the `Object` structure stream. Technically it's a + // set of all `Object` structure's fields except `payload`. + message Init { + // Object's unique identifier. + neo.fs.v2.refs.ObjectID object_id = 1; + + // Signed `ObjectID` + neo.fs.v2.refs.Signature signature = 2; + + // Object metadata headers + Header header = 3; + } + // Single message in the response stream. + oneof object_part { + // Initial part of the object stream + Init init = 1; + + // Chunked object payload + bytes chunk = 2; + + // Meta information of split hierarchy for object assembly. + SplitInfo split_info = 3; + + // Meta information for EC object assembly. + ECInfo ec_info = 4; + } + } + // Body of get object response message. + Body body = 1; + + // Carries response meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.ResponseMetaHeader meta_header = 2; + + // Carries response verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; +} + +// PUT object request +message PutRequest { + // PUT request body + message Body { + // Newly created object structure parameters. If some optional parameters + // are not set, they will be calculated by a peer node. + message Init { + // ObjectID if available. + neo.fs.v2.refs.ObjectID object_id = 1; + + // Object signature if available + neo.fs.v2.refs.Signature signature = 2; + + // Object's Header + Header header = 3; + + // Number of copies of the object to store within the RPC call. By + // default, object is processed according to the container's placement + // policy. Can be one of: + // 1. A single number; applied to the whole request and is treated as + // a minimal number of nodes that must store an object to complete the + // request successfully. + // 2. An ordered array; every number is treated as a minimal number of + // nodes in a corresponding placement vector that must store an object + // to complete the request successfully. The length MUST equal the + // placement vectors number, otherwise request is considered malformed. + repeated uint32 copies_number = 4; + } + // Single message in the request stream. + oneof object_part { + // Initial part of the object stream + Init init = 1; + + // Chunked object payload + bytes chunk = 2; + } + } + // Body of put object request message. + Body body = 1; + + // Carries request meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.RequestMetaHeader meta_header = 2; + + // Carries request verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.RequestVerificationHeader verify_header = 3; +} + +// PUT Object response +message PutResponse { + // PUT Object response body + message Body { + // Identifier of the saved object + neo.fs.v2.refs.ObjectID object_id = 1; + } + // Body of put object response message. + Body body = 1; + + // Carries response meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.ResponseMetaHeader meta_header = 2; + + // Carries response verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; +} + +// Object DELETE request +message DeleteRequest { + // Object DELETE request body + message Body { + // Address of the object to be deleted + neo.fs.v2.refs.Address address = 1; + } + // Body of delete object request message. + Body body = 1; + + // Carries request meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.RequestMetaHeader meta_header = 2; + + // Carries request verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.RequestVerificationHeader verify_header = 3; +} + +// DeleteResponse body is empty because we cannot guarantee permanent object +// removal in distributed system. +message DeleteResponse { + // Object DELETE Response has an empty body. + message Body { + // Address of the tombstone created for the deleted object + neo.fs.v2.refs.Address tombstone = 1; + } + + // Body of delete object response message. + Body body = 1; + + // Carries response meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.ResponseMetaHeader meta_header = 2; + + // Carries response verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; +} + +// Object HEAD request +message HeadRequest { + // Object HEAD request body + message Body { + // Address of the object with the requested Header + neo.fs.v2.refs.Address address = 1; + + // Return only minimal header subset + bool main_only = 2; + + // If `raw` flag is set, request will work only with objects that are + // physically stored on the peer node + bool raw = 3; + } + // Body of head object request message. + Body body = 1; + + // Carries request meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.RequestMetaHeader meta_header = 2; + + // Carries request verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.RequestVerificationHeader verify_header = 3; +} + +// Tuple of a full object header and signature of an `ObjectID`. \ +// Signed `ObjectID` is present to verify full header's authenticity through the +// following steps: +// +// 1. Calculate `SHA-256` of the marshalled `Header` structure +// 2. Check if the resulting hash matches `ObjectID` +// 3. Check if `ObjectID` signature in `signature` field is correct +message HeaderWithSignature { + // Full object header + Header header = 1 [ json_name = "header" ]; + + // Signed `ObjectID` to verify full header's authenticity + neo.fs.v2.refs.Signature signature = 2 [ json_name = "signature" ]; +} + +// Object HEAD response +message HeadResponse { + // Object HEAD response body + message Body { + // Requested object header, it's part or meta information about split + // object. + oneof head { + // Full object's `Header` with `ObjectID` signature + HeaderWithSignature header = 1; + + // Short object header + ShortHeader short_header = 2; + + // Meta information of split hierarchy. + SplitInfo split_info = 3; + + // Meta information for EC object assembly. + ECInfo ec_info = 4; + } + } + // Body of head object response message. + Body body = 1; + + // Carries response meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.ResponseMetaHeader meta_header = 2; + + // Carries response verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; +} + +// Object Search request +message SearchRequest { + // Object Search request body + message Body { + // Container identifier were to search + neo.fs.v2.refs.ContainerID container_id = 1; + + // Version of the Query Language used + uint32 version = 2; + // Filter structure checks if the object header field or the attribute + // content matches a value. + // + // If no filters are set, search request will return all objects of the + // container, including Regular object and Tombstone + // objects. Most human users expect to get only object they can directly + // work with. In that case, `$Object:ROOT` filter should be used. + // + // By default `key` field refers to the corresponding object's `Attribute`. + // Some Object's header fields can also be accessed by adding `$Object:` + // prefix to the name. Here is the list of fields available via this prefix: + // + // * $Object:version \ + // version + // * $Object:objectID \ + // object_id + // * $Object:containerID \ + // container_id + // * $Object:ownerID \ + // owner_id + // * $Object:creationEpoch \ + // creation_epoch + // * $Object:payloadLength \ + // payload_length + // * $Object:payloadHash \ + // payload_hash + // * $Object:objectType \ + // object_type + // * $Object:homomorphicHash \ + // homomorphic_hash + // * $Object:split.parent \ + // object_id of parent + // * $Object:split.splitID \ + // 16 byte UUIDv4 used to identify the split object hierarchy parts + // + // There are some well-known filter aliases to match objects by certain + // properties: + // + // * $Object:ROOT \ + // Returns only `REGULAR` type objects that are not split or that are the + // top level root objects in a split hierarchy. This includes objects not + // present physically, like large objects split into smaller objects + // without a separate top-level root object. Objects of other types like + // Locks and Tombstones will not be shown. This filter may be + // useful for listing objects like `ls` command of some virtual file + // system. This filter is activated if the `key` exists, disregarding the + // value and matcher type. + // * $Object:PHY \ + // Returns only objects physically stored in the system. This filter is + // activated if the `key` exists, disregarding the value and matcher type. + // + // Note: using filters with a key with prefix `$Object:` and match type + // `NOT_PRESENT `is not recommended since this is not a cross-version + // approach. Behavior when processing this kind of filters is undefined. + message Filter { + // Match type to use + MatchType match_type = 1 [ json_name = "matchType" ]; + + // Attribute or Header fields to match + string key = 2 [ json_name = "key" ]; + + // Value to match + string value = 3 [ json_name = "value" ]; + } + // List of search expressions + repeated Filter filters = 3; + } + // Body of search object request message. + Body body = 1; + + // Carries request meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.RequestMetaHeader meta_header = 2; + + // Carries request verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.RequestVerificationHeader verify_header = 3; +} + +// Search response +message SearchResponse { + // Object Search response body + message Body { + // List of `ObjectID`s that match the search query + repeated neo.fs.v2.refs.ObjectID id_list = 1; + } + // Body of search object response message. + Body body = 1; + + // Carries response meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.ResponseMetaHeader meta_header = 2; + + // Carries response verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; +} + +// Object payload range.Ranges of zero length SHOULD be considered as invalid. +message Range { + // Offset of the range from the object payload start + uint64 offset = 1; + + // Length in bytes of the object payload range + uint64 length = 2; +} + +// Request part of object's payload +message GetRangeRequest { + // Byte range of object's payload request body + message Body { + // Address of the object containing the requested payload range + neo.fs.v2.refs.Address address = 1; + + // Requested payload range + Range range = 2; + + // If `raw` flag is set, request will work only with objects that are + // physically stored on the peer node. + bool raw = 3; + } + + // Body of get range object request message. + Body body = 1; + + // Carries request meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.RequestMetaHeader meta_header = 2; + + // Carries request verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.RequestVerificationHeader verify_header = 3; +} + +// Get part of object's payload +message GetRangeResponse { + // Get Range response body uses streams to transfer the response. Because + // object payload considered a byte sequence, there is no need to have some + // initial preamble message. The requested byte range is sent as a series + // chunks. + message Body { + // Requested object range or meta information about split object. + oneof range_part { + // Chunked object payload's range. + bytes chunk = 1; + + // Meta information of split hierarchy. + SplitInfo split_info = 2; + + // Meta information for EC object assembly. + ECInfo ec_info = 3; + } + } + + // Body of get range object response message. + Body body = 1; + + // Carries response meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.ResponseMetaHeader meta_header = 2; + + // Carries response verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; +} + +// Get hash of object's payload part +message GetRangeHashRequest { + // Get hash of object's payload part request body. + message Body { + // Address of the object that containing the requested payload range + neo.fs.v2.refs.Address address = 1; + + // List of object's payload ranges to calculate homomorphic hash + repeated Range ranges = 2; + + // Binary salt to XOR object's payload ranges before hash calculation + bytes salt = 3; + + // Checksum algorithm type + neo.fs.v2.refs.ChecksumType type = 4; + } + // Body of get range hash object request message. + Body body = 1; + + // Carries request meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.RequestMetaHeader meta_header = 2; + + // Carries request verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.RequestVerificationHeader verify_header = 3; +} + +// Get hash of object's payload part +message GetRangeHashResponse { + // Get hash of object's payload part response body. + message Body { + // Checksum algorithm type + neo.fs.v2.refs.ChecksumType type = 1; + + // List of range hashes in a binary format + repeated bytes hash_list = 2; + } + // Body of get range hash object response message. + Body body = 1; + + // Carries response meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.ResponseMetaHeader meta_header = 2; + + // Carries response verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; +} + +// Object PUT Single request +message PutSingleRequest { + // PUT Single request body + message Body { + // Prepared object with payload. + Object object = 1; + // Number of copies of the object to store within the RPC call. By default, + // object is processed according to the container's placement policy. + // Every number is treated as a minimal number of + // nodes in a corresponding placement vector that must store an object + // to complete the request successfully. The length MUST equal the placement + // vectors number, otherwise request is considered malformed. + repeated uint32 copies_number = 2; + } + // Body of put single object request message. + Body body = 1; + + // Carries request meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.RequestMetaHeader meta_header = 2; + + // Carries request verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.RequestVerificationHeader verify_header = 3; +} + +// Object PUT Single response +message PutSingleResponse { + // PUT Single Object response body + message Body {} + // Body of put single object response message. + Body body = 1; + + // Carries response meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.ResponseMetaHeader meta_header = 2; + + // Carries response verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; +} \ No newline at end of file diff --git a/protosV2/src/main/proto/object/types.proto b/protosV2/src/main/proto/object/types.proto new file mode 100644 index 0000000..3162270 --- /dev/null +++ b/protosV2/src/main/proto/object/types.proto @@ -0,0 +1,266 @@ +syntax = "proto3"; + +package neo.fs.v2.object; + +option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object/grpc;object"; +option java_package = "frostFS.object"; + +import "refs/types.proto"; +import "session/types.proto"; + +// Type of the object payload content. Only `REGULAR` type objects can be split, +// hence `TOMBSTONE` and `LOCK` payload is limited by the +// maximum object size. +// +// String presentation of object type is the same as definition: +// * REGULAR +// * TOMBSTONE +// * LOCK +enum ObjectType { + // Just a normal object + REGULAR = 0; + + // Used internally to identify deleted objects + TOMBSTONE = 1; + + // Unused (previously storageGroup information) + // _ = 2; + + // Object lock + LOCK = 3; +} + +// Type of match expression +enum MatchType { + // Unknown. Not used + MATCH_TYPE_UNSPECIFIED = 0; + + // Full string match + STRING_EQUAL = 1; + + // Full string mismatch + STRING_NOT_EQUAL = 2; + + // Lack of key + NOT_PRESENT = 3; + + // String prefix match + COMMON_PREFIX = 4; +} + +// Short header fields +message ShortHeader { + // Object format version. Effectively, the version of API library used to + // create particular object. + neo.fs.v2.refs.Version version = 1 [ json_name = "version" ]; + + // Epoch when the object was created + uint64 creation_epoch = 2 [ json_name = "creationEpoch" ]; + + // Object's owner + neo.fs.v2.refs.OwnerID owner_id = 3 [ json_name = "ownerID" ]; + + // Type of the object payload content + ObjectType object_type = 4 [ json_name = "objectType" ]; + + // Size of payload in bytes. + // `0xFFFFFFFFFFFFFFFF` means `payload_length` is unknown + uint64 payload_length = 5 [ json_name = "payloadLength" ]; + + // Hash of payload bytes + neo.fs.v2.refs.Checksum payload_hash = 6 [ json_name = "payloadHash" ]; + + // Homomorphic hash of the object payload + neo.fs.v2.refs.Checksum homomorphic_hash = 7 + [ json_name = "homomorphicHash" ]; +} + +// Object Header +message Header { + // Object format version. Effectively, the version of API library used to + // create particular object + neo.fs.v2.refs.Version version = 1 [ json_name = "version" ]; + + // Object's container + neo.fs.v2.refs.ContainerID container_id = 2 [ json_name = "containerID" ]; + + // Object's owner + neo.fs.v2.refs.OwnerID owner_id = 3 [ json_name = "ownerID" ]; + + // Object creation Epoch + uint64 creation_epoch = 4 [ json_name = "creationEpoch" ]; + + // Size of payload in bytes. + // `0xFFFFFFFFFFFFFFFF` means `payload_length` is unknown. + uint64 payload_length = 5 [ json_name = "payloadLength" ]; + + // Hash of payload bytes + neo.fs.v2.refs.Checksum payload_hash = 6 [ json_name = "payloadHash" ]; + + // Type of the object payload content + ObjectType object_type = 7 [ json_name = "objectType" ]; + + // Homomorphic hash of the object payload + neo.fs.v2.refs.Checksum homomorphic_hash = 8 + [ json_name = "homomorphicHash" ]; + + // Session token, if it was used during Object creation. Need it to verify + // integrity and authenticity out of Request scope. + neo.fs.v2.session.SessionToken session_token = 9 + [ json_name = "sessionToken" ]; + + // `Attribute` is a user-defined Key-Value metadata pair attached to an + // object. + // + // Key name must be an object-unique valid UTF-8 string. Value can't be empty. + // Objects with duplicated attribute names or attributes with empty values + // will be considered invalid. + // + // There are some "well-known" attributes starting with `__SYSTEM__` + // (`__NEOFS__` is deprecated) prefix that affect system behaviour: + // + // * [ __SYSTEM__UPLOAD_ID ] \ + // (`__NEOFS__UPLOAD_ID` is deprecated) \ + // Marks smaller parts of a split bigger object + // * [ __SYSTEM__EXPIRATION_EPOCH ] \ + // (`__NEOFS__EXPIRATION_EPOCH` is deprecated) \ + // The epoch after which object with no LOCKs on it becomes unavailable. + // Locked object continues to be available until each of the LOCKs expire. + // * [ __SYSTEM__TICK_EPOCH ] \ + // (`__NEOFS__TICK_EPOCH` is deprecated) \ + // Decimal number that defines what epoch must produce + // object notification with UTF-8 object address in a + // body (`0` value produces notification right after + // object put) + // * [ __SYSTEM__TICK_TOPIC ] \ + // (`__NEOFS__TICK_TOPIC` is deprecated) \ + // UTF-8 string topic ID that is used for object notification + // + // And some well-known attributes used by applications only: + // + // * Name \ + // Human-friendly name + // * FileName \ + // File name to be associated with the object on saving + // * FilePath \ + // Full path to be associated with the object on saving. Should start with a + // '/' and use '/' as a delimiting symbol. Trailing '/' should be + // interpreted as a virtual directory marker. If an object has conflicting + // FilePath and FileName, FilePath should have higher priority, because it + // is used to construct the directory tree. FilePath with trailing '/' and + // non-empty FileName attribute should not be used together. + // * Timestamp \ + // User-defined local time of object creation in Unix Timestamp format + // * Content-Type \ + // MIME Content Type of object's payload + // + // For detailed description of each well-known attribute please see the + // corresponding section in NeoFS Technical Specification. + message Attribute { + // string key to the object attribute + string key = 1 [ json_name = "key" ]; + // string value of the object attribute + string value = 2 [ json_name = "value" ]; + } + // User-defined object attributes + repeated Attribute attributes = 10 [ json_name = "attributes" ]; + + // Bigger objects can be split into a chain of smaller objects. Information + // about inter-dependencies between spawned objects and how to re-construct + // the original one is in the `Split` headers. Parent and children objects + // must be within the same container. + message Split { + // Identifier of the origin object. Known only to the minor child. + neo.fs.v2.refs.ObjectID parent = 1 [ json_name = "parent" ]; + + // Identifier of the left split neighbor + neo.fs.v2.refs.ObjectID previous = 2 [ json_name = "previous" ]; + + // `signature` field of the parent object. Used to reconstruct parent. + neo.fs.v2.refs.Signature parent_signature = 3 + [ json_name = "parentSignature" ]; + + // `header` field of the parent object. Used to reconstruct parent. + Header parent_header = 4 [ json_name = "parentHeader" ]; + + // List of identifiers of the objects generated by splitting current one. + repeated neo.fs.v2.refs.ObjectID children = 5 [ json_name = "children" ]; + + // 16 byte UUIDv4 used to identify the split object hierarchy parts. Must be + // unique inside container. All objects participating in the split must have + // the same `split_id` value. + bytes split_id = 6 [ json_name = "splitID" ]; + } + // Position of the object in the split hierarchy + Split split = 11 [ json_name = "split" ]; + + // Erasure code can be applied to any object. + // Information about encoded object structure is stored in `EC` header. + // All objects belonging to a single EC group have the same `parent` field. + message EC { + // Identifier of the origin object. Known to all chunks. + neo.fs.v2.refs.ObjectID parent = 1 [ json_name = "parent" ]; + // Index of this chunk. + uint32 index = 2 [ json_name = "index" ]; + // Total number of chunks in this split. + uint32 total = 3 [ json_name = "total" ]; + // Total length of a parent header. Used to trim padding zeroes. + uint32 header_length = 4 [ json_name = "headerLength" ]; + // Chunk of a parent header. + bytes header = 5 [ json_name = "header" ]; + } + // Erasure code chunk information. + EC ec = 12 [ json_name = "ec" ]; +} + +// Object structure. Object is immutable and content-addressed. It means +// `ObjectID` will change if the header or the payload changes. It's calculated +// as a hash of header field which contains hash of the object's payload. +// +// For non-regular object types payload format depends on object type specified +// in the header. +message Object { + // Object's unique identifier. + neo.fs.v2.refs.ObjectID object_id = 1 [ json_name = "objectID" ]; + + // Signed object_id + neo.fs.v2.refs.Signature signature = 2 [ json_name = "signature" ]; + + // Object metadata headers + Header header = 3 [ json_name = "header" ]; + + // Payload bytes + bytes payload = 4 [ json_name = "payload" ]; +} + +// Meta information of split hierarchy for object assembly. With the last part +// one can traverse linked list of split hierarchy back to the first part and +// assemble the original object. With a linking object one can assemble an +// object right from the object parts. +message SplitInfo { + // 16 byte UUID used to identify the split object hierarchy parts. + bytes split_id = 1; + + // The identifier of the last object in split hierarchy parts. It contains + // split header with the original object header. + neo.fs.v2.refs.ObjectID last_part = 2; + + // The identifier of a linking object for split hierarchy parts. It contains + // split header with the original object header and a sorted list of + // object parts. + neo.fs.v2.refs.ObjectID link = 3; +} + +// Meta information for the erasure-encoded object. +message ECInfo { + message Chunk { + // Object ID of the chunk. + neo.fs.v2.refs.ObjectID id = 1; + // Index of the chunk. + uint32 index = 2; + // Total number of chunks in this split. + uint32 total = 3; + } + // Chunk stored on the node. + repeated Chunk chunks = 1; +} \ No newline at end of file diff --git a/protosV2/src/main/proto/refs/types.proto b/protosV2/src/main/proto/refs/types.proto new file mode 100644 index 0000000..30cb552 --- /dev/null +++ b/protosV2/src/main/proto/refs/types.proto @@ -0,0 +1,150 @@ +syntax = "proto3"; + +package neo.fs.v2.refs; + +option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs/grpc;refs"; +option java_package = "frostFS.refs"; + +// Objects in NeoFS are addressed by their ContainerID and ObjectID. +// +// String presentation of `Address` is a concatenation of string encoded +// `ContainerID` and `ObjectID` delimited by '/' character. +message Address { + // Container identifier + ContainerID container_id = 1 [ json_name = "containerID" ]; + // Object identifier + ObjectID object_id = 2 [ json_name = "objectID" ]; +} + +// NeoFS Object unique identifier. Objects are immutable and content-addressed. +// It means `ObjectID` will change if the `header` or the `payload` changes. +// +// `ObjectID` is a 32 byte long +// [SHA256](https://csrc.nist.gov/publications/detail/fips/180/4/final) hash of +// the object's `header` field, which, in it's turn, contains the hash of the +// object's payload. +// +// String presentation is a +// [base58](https://tools.ietf.org/html/draft-msporny-base58-02) encoded string. +// +// JSON value will be data encoded as a string using standard base64 +// encoding with paddings. Either +// [standard](https://tools.ietf.org/html/rfc4648#section-4) or +// [URL-safe](https://tools.ietf.org/html/rfc4648#section-5) base64 encoding +// with/without paddings are accepted. +message ObjectID { + // Object identifier in a binary format + bytes value = 1 [ json_name = "value" ]; +} + +// NeoFS container identifier. Container structures are immutable and +// content-addressed. +// +// `ContainerID` is a 32 byte long +// [SHA256](https://csrc.nist.gov/publications/detail/fips/180/4/final) hash of +// stable-marshalled container message. +// +// String presentation is a +// [base58](https://tools.ietf.org/html/draft-msporny-base58-02) encoded string. +// +// JSON value will be data encoded as a string using standard base64 +// encoding with paddings. Either +// [standard](https://tools.ietf.org/html/rfc4648#section-4) or +// [URL-safe](https://tools.ietf.org/html/rfc4648#section-5) base64 encoding +// with/without paddings are accepted. +message ContainerID { + // Container identifier in a binary format. + bytes value = 1 [ json_name = "value" ]; +} + +// `OwnerID` is a derivative of a user's main public key. The transformation +// algorithm is the same as for Neo3 wallet addresses. Neo3 wallet address can +// be directly used as `OwnerID`. +// +// `OwnerID` is a 25 bytes sequence starting with Neo version prefix byte +// followed by 20 bytes of ScrptHash and 4 bytes of checksum. +// +// String presentation is a [Base58 +// Check](https://en.bitcoin.it/wiki/Base58Check_encoding) Encoded string. +// +// JSON value will be data encoded as a string using standard base64 +// encoding with paddings. Either +// [standard](https://tools.ietf.org/html/rfc4648#section-4) or +// [URL-safe](https://tools.ietf.org/html/rfc4648#section-5) base64 encoding +// with/without paddings are accepted. +message OwnerID { + // Identifier of the container owner in a binary format + bytes value = 1 [ json_name = "value" ]; +} + +// API version used by a node. +// +// String presentation is a Semantic Versioning 2.0.0 compatible version string +// with 'v' prefix. i.e. `vX.Y`, where `X` is the major number, `Y` is the minor +// number. +message Version { + // Major API version + uint32 major = 1 [ json_name = "major" ]; + + // Minor API version + uint32 minor = 2 [ json_name = "minor" ]; +} + +// Signature of something in NeoFS. +message Signature { + // Public key used for signing + bytes key = 1 [ json_name = "key" ]; + // Signature + bytes sign = 2 [ json_name = "signature" ]; + // Scheme contains digital signature scheme identifier + SignatureScheme scheme = 3 [ json_name = "scheme" ]; +} + +// Signature scheme describes digital signing scheme used for (key, signature) +// pair. +enum SignatureScheme { + // ECDSA with SHA-512 hashing (FIPS 186-3) + ECDSA_SHA512 = 0; + + // Deterministic ECDSA with SHA-256 hashing (RFC 6979) + ECDSA_RFC6979_SHA256 = 1; + + // Deterministic ECDSA with SHA-256 hashing using WalletConnect API. + // Here the algorithm is the same, but the message format differs. + ECDSA_RFC6979_SHA256_WALLET_CONNECT = 2; +} + +// RFC 6979 signature. +message SignatureRFC6979 { + // Public key used for signing + bytes key = 1 [ json_name = "key" ]; + // Deterministic ECDSA with SHA-256 hashing + bytes sign = 2 [ json_name = "signature" ]; +} + +// Checksum algorithm type. +enum ChecksumType { + // Unknown. Not used + CHECKSUM_TYPE_UNSPECIFIED = 0; + + // Tillich-Zemor homomorphic hash function + TZ = 1; + + // SHA-256 + SHA256 = 2; +} + +// Checksum message. +// Depending on checksum algorithm type, the string presentation may vary: +// +// * TZ \ +// Hex encoded string without `0x` prefix +// * SHA256 \ +// Hex encoded string without `0x` prefix +message Checksum { + // Checksum algorithm type + ChecksumType type = 1 [ json_name = "type" ]; + + // Checksum itself + bytes sum = 2 [ json_name = "sum" ]; +} diff --git a/protosV2/src/main/proto/session/service.proto b/protosV2/src/main/proto/session/service.proto new file mode 100644 index 0000000..84cbff4 --- /dev/null +++ b/protosV2/src/main/proto/session/service.proto @@ -0,0 +1,69 @@ +syntax = "proto3"; + +package neo.fs.v2.session; + +option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session/grpc;session"; +option java_package = "frostFS.session"; + +import "refs/types.proto"; +import "session/types.proto"; + +// `SessionService` allows to establish a temporary trust relationship between +// two peer nodes and generate a `SessionToken` as the proof of trust to be +// attached in requests for further verification. Please see corresponding +// section of NeoFS Technical Specification for details. +service SessionService { + // Open a new session between two peers. + // + // Statuses: + // - **OK** (0, SECTION_SUCCESS): + // session has been successfully opened; + // - Common failures (SECTION_FAILURE_COMMON). + rpc Create(CreateRequest) returns (CreateResponse); +} + +// Information necessary for opening a session. +message CreateRequest { + // Session creation request body + message Body { + // Session initiating user's or node's key derived `OwnerID` + neo.fs.v2.refs.OwnerID owner_id = 1; + // Session expiration `Epoch` + uint64 expiration = 2; + } + // Body of a create session token request message. + Body body = 1; + + // Carries request meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.RequestMetaHeader meta_header = 2; + + // Carries request verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.RequestVerificationHeader verify_header = 3; +} + +// Information about the opened session. +message CreateResponse { + // Session creation response body + message Body { + // Identifier of a newly created session + bytes id = 1; + + // Public key used for session + bytes session_key = 2; + } + + // Body of create session token response message. + Body body = 1; + + // Carries response meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.ResponseMetaHeader meta_header = 2; + + // Carries response verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; +} diff --git a/protosV2/src/main/proto/session/types.proto b/protosV2/src/main/proto/session/types.proto new file mode 100644 index 0000000..2b33386 --- /dev/null +++ b/protosV2/src/main/proto/session/types.proto @@ -0,0 +1,238 @@ +syntax = "proto3"; + +package neo.fs.v2.session; + +option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session/grpc;session"; +option java_package = "frostFS.session"; + +import "refs/types.proto"; +import "acl/types.proto"; +import "status/types.proto"; + +// Context information for Session Tokens related to ObjectService requests +message ObjectSessionContext { + // Object request verbs + enum Verb { + // Unknown verb + VERB_UNSPECIFIED = 0; + + // Refers to object.Put RPC call + PUT = 1; + + // Refers to object.Get RPC call + GET = 2; + + // Refers to object.Head RPC call + HEAD = 3; + + // Refers to object.Search RPC call + SEARCH = 4; + + // Refers to object.Delete RPC call + DELETE = 5; + + // Refers to object.GetRange RPC call + RANGE = 6; + + // Refers to object.GetRangeHash RPC call + RANGEHASH = 7; + } + // Type of request for which the token is issued + Verb verb = 1 [ json_name = "verb" ]; + + // Carries objects involved in the object session. + message Target { + // Indicates which container the session is spread to. Field MUST be set + // and correct. + refs.ContainerID container = 1 [ json_name = "container" ]; + + // Indicates which objects the session is spread to. Objects are expected + // to be stored in the NeoFS container referenced by `container` field. + // Each element MUST have correct format. + repeated refs.ObjectID objects = 2 [ json_name = "objects" ]; + } + // Object session target. MUST be correctly formed and set. If `objects` + // field is not empty, then the session applies only to these elements, + // otherwise, to all objects from the specified container. + Target target = 2 [ json_name = "target" ]; +} + +// Context information for Session Tokens related to ContainerService requests. +message ContainerSessionContext { + // Container request verbs + enum Verb { + // Unknown verb + VERB_UNSPECIFIED = 0; + + // Refers to container.Put RPC call + PUT = 1; + + // Refers to container.Delete RPC call + DELETE = 2; + + // Refers to container.SetExtendedACL RPC call + SETEACL = 3; + } + // Type of request for which the token is issued + Verb verb = 1 [ json_name = "verb" ]; + + // Spreads the action to all owner containers. + // If set, container_id field is ignored. + bool wildcard = 2 [ json_name = "wildcard" ]; + + // Particular container to which the action applies. + // Ignored if wildcard flag is set. + refs.ContainerID container_id = 3 [ json_name = "containerID" ]; +} + +// NeoFS Session Token. +message SessionToken { + // Session Token body + message Body { + // Token identifier is a valid UUIDv4 in binary form + bytes id = 1 [ json_name = "id" ]; + + // Identifier of the session initiator + neo.fs.v2.refs.OwnerID owner_id = 2 [ json_name = "ownerID" ]; + + // Lifetime parameters of the token. Field names taken from rfc7519. + message TokenLifetime { + // Expiration Epoch + uint64 exp = 1 [ json_name = "exp" ]; + + // Not valid before Epoch + uint64 nbf = 2 [ json_name = "nbf" ]; + + // Issued at Epoch + uint64 iat = 3 [ json_name = "iat" ]; + } + // Lifetime of the session + TokenLifetime lifetime = 3 [ json_name = "lifetime" ]; + + // Public key used in session + bytes session_key = 4 [ json_name = "sessionKey" ]; + + // Session Context information + oneof context { + // ObjectService session context + ObjectSessionContext object = 5 [ json_name = "object" ]; + + // ContainerService session context + ContainerSessionContext container = 6 [ json_name = "container" ]; + } + } + // Session Token contains the proof of trust between peers to be attached in + // requests for further verification. Please see corresponding section of + // NeoFS Technical Specification for details. + Body body = 1 [ json_name = "body" ]; + + // Signature of `SessionToken` information + neo.fs.v2.refs.Signature signature = 2 [ json_name = "signature" ]; +} + +// Extended headers for Request/Response. They may contain any user-defined +// headers to be interpreted on application level. +// +// Key name must be a unique valid UTF-8 string. Value can't be empty. Requests +// or Responses with duplicated header names or headers with empty values will +// be considered invalid. +// +// There are some "well-known" headers starting with `__SYSTEM__` (`__NEOFS__` +// is deprecated) prefix that affect system behaviour: +// +// * [ __SYSTEM__NETMAP_EPOCH ] \ +// (`__NEOFS__NETMAP_EPOCH` is deprecated) \ +// Netmap epoch to use for object placement calculation. The `value` is string +// encoded `uint64` in decimal presentation. If set to '0' or not set, the +// current epoch only will be used. +// * [ __SYSTEM__NETMAP_LOOKUP_DEPTH ] \ +// (`__NEOFS__NETMAP_LOOKUP_DEPTH` is deprecated) \ +// If object can't be found using current epoch's netmap, this header limits +// how many past epochs the node can look up through. The `value` is string +// encoded `uint64` in decimal presentation. If set to '0' or not set, only +// the current epoch will be used. +message XHeader { + // Key of the X-Header + string key = 1 [ json_name = "key" ]; + + // Value of the X-Header + string value = 2 [ json_name = "value" ]; +} + +// Meta information attached to the request. When forwarded between peers, +// request meta headers are folded in matryoshka style. +message RequestMetaHeader { + // Peer's API version used + neo.fs.v2.refs.Version version = 1 [ json_name = "version" ]; + + // Peer's local epoch number. Set to 0 if unknown. + uint64 epoch = 2 [ json_name = "epoch" ]; + + // Maximum number of intermediate nodes in the request route + uint32 ttl = 3 [ json_name = "ttl" ]; + + // Request X-Headers + repeated XHeader x_headers = 4 [ json_name = "xHeaders" ]; + + // Session token within which the request is sent + SessionToken session_token = 5 [ json_name = "sessionToken" ]; + + // `BearerToken` with eACL overrides for the request + neo.fs.v2.acl.BearerToken bearer_token = 6 [ json_name = "bearerToken" ]; + + // `RequestMetaHeader` of the origin request + RequestMetaHeader origin = 7 [ json_name = "origin" ]; + + // NeoFS network magic. Must match the value for the network + // that the server belongs to. + uint64 magic_number = 8 [ json_name = "magicNumber" ]; +} + +// Information about the response +message ResponseMetaHeader { + // Peer's API version used + neo.fs.v2.refs.Version version = 1 [ json_name = "version" ]; + + // Peer's local epoch number + uint64 epoch = 2 [ json_name = "epoch" ]; + + // Maximum number of intermediate nodes in the request route + uint32 ttl = 3 [ json_name = "ttl" ]; + + // Response X-Headers + repeated XHeader x_headers = 4 [ json_name = "xHeaders" ]; + + // `ResponseMetaHeader` of the origin request + ResponseMetaHeader origin = 5 [ json_name = "origin" ]; + + // Status return + neo.fs.v2.status.Status status = 6 [ json_name = "status" ]; +} + +// Verification info for the request signed by all intermediate nodes. +message RequestVerificationHeader { + // Request Body signature. Should be generated once by the request initiator. + neo.fs.v2.refs.Signature body_signature = 1 [ json_name = "bodySignature" ]; + // Request Meta signature is added and signed by each intermediate node + neo.fs.v2.refs.Signature meta_signature = 2 [ json_name = "metaSignature" ]; + // Signature of previous hops + neo.fs.v2.refs.Signature origin_signature = 3 + [ json_name = "originSignature" ]; + + // Chain of previous hops signatures + RequestVerificationHeader origin = 4 [ json_name = "origin" ]; +} + +// Verification info for the response signed by all intermediate nodes +message ResponseVerificationHeader { + // Response Body signature. Should be generated once by an answering node. + neo.fs.v2.refs.Signature body_signature = 1 [ json_name = "bodySignature" ]; + // Response Meta signature is added and signed by each intermediate node + neo.fs.v2.refs.Signature meta_signature = 2 [ json_name = "metaSignature" ]; + // Signature of previous hops + neo.fs.v2.refs.Signature origin_signature = 3 + [ json_name = "originSignature" ]; + + // Chain of previous hops signatures + ResponseVerificationHeader origin = 4 [ json_name = "origin" ]; +} diff --git a/protosV2/src/main/proto/status/types.proto b/protosV2/src/main/proto/status/types.proto new file mode 100644 index 0000000..b7be372 --- /dev/null +++ b/protosV2/src/main/proto/status/types.proto @@ -0,0 +1,157 @@ +syntax = "proto3"; + +package neo.fs.v2.status; + +option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/status/grpc;status"; +option java_package = "frostFS.status"; + +// Declares the general format of the status returns of the NeoFS RPC protocol. +// Status is present in all response messages. Each RPC of NeoFS protocol +// describes the possible outcomes and details of the operation. +// +// Each status is assigned a one-to-one numeric code. Any unique result of an +// operation in NeoFS is unambiguously associated with the code value. +// +// Numerical set of codes is split into 1024-element sections. An enumeration +// is defined for each section. Values can be referred to in the following ways: +// +// * numerical value ranging from 0 to 4,294,967,295 (global code); +// +// * values from enumeration (local code). The formula for the ratio of the +// local code (`L`) of a defined section (`S`) to the global one (`G`): +// `G = 1024 * S + L`. +// +// All outcomes are divided into successful and failed, which corresponds +// to the success or failure of the operation. The definition of success +// follows the semantics of RPC and the description of its purpose. +// The server must not attach code that is the opposite of the outcome type. +// +// See the set of return codes in the description for calls. +// +// Each status can carry a developer-facing error message. It should be a human +// readable text in English. The server should not transmit (and the client +// should not expect) useful information in the message. Field `details` +// should make the return more detailed. +message Status { + // The status code + uint32 code = 1; + + // Developer-facing error message + string message = 2; + + // Return detail. It contains additional information that can be used to + // analyze the response. Each code defines a set of details that can be + // attached to a status. Client should not handle details that are not + // covered by the code. + message Detail { + // Detail ID. The identifier is required to determine the binary format + // of the detail and how to decode it. + uint32 id = 1; + + // Binary status detail. Must follow the format associated with ID. + // The possibility of missing a value must be explicitly allowed. + bytes value = 2; + } + + // Data detailing the outcome of the operation. Must be unique by ID. + repeated Detail details = 3; +} + +// Section identifiers. +enum Section { + // Successful return codes. + SECTION_SUCCESS = 0; + + // Failure codes regardless of the operation. + SECTION_FAILURE_COMMON = 1; + + // Object service-specific errors. + SECTION_OBJECT = 2; + + // Container service-specific errors. + SECTION_CONTAINER = 3; + + // Session service-specific errors. + SECTION_SESSION = 4; + + // Session service-specific errors. + SECTION_APE_MANAGER = 5; +} + +// Section of NeoFS successful return codes. +enum Success { + // [**0**] Default success. Not detailed. + // If the server cannot match successful outcome to the code, it should + // use this code. + OK = 0; +} + +// Section of failed statuses independent of the operation. +enum CommonFail { + // [**1024**] Internal server error, default failure. Not detailed. + // If the server cannot match failed outcome to the code, it should + // use this code. + INTERNAL = 0; + + // [**1025**] Wrong magic of the NeoFS network. + // Details: + // - [**0**] Magic number of the served NeoFS network (big-endian 64-bit + // unsigned integer). + WRONG_MAGIC_NUMBER = 1; + + // [**1026**] Signature verification failure. + SIGNATURE_VERIFICATION_FAIL = 2; + + // [**1027**] Node is under maintenance. + NODE_UNDER_MAINTENANCE = 3; +} + +// Section of statuses for object-related operations. +enum Object { + // [**2048**] Access denied by ACL. + // Details: + // - [**0**] Human-readable description (UTF-8 encoded string). + ACCESS_DENIED = 0; + + // [**2049**] Object not found. + OBJECT_NOT_FOUND = 1; + + // [**2050**] Operation rejected by the object lock. + LOCKED = 2; + + // [**2051**] Locking an object with a non-REGULAR type rejected. + LOCK_NON_REGULAR_OBJECT = 3; + + // [**2052**] Object has been marked deleted. + OBJECT_ALREADY_REMOVED = 4; + + // [**2053**] Invalid range has been requested for an object. + OUT_OF_RANGE = 5; +} + +// Section of statuses for container-related operations. +enum Container { + // [**3072**] Container not found. + CONTAINER_NOT_FOUND = 0; + + // [**3073**] eACL table not found. + EACL_NOT_FOUND = 1; + + // [**3074**] Container access denied. + CONTAINER_ACCESS_DENIED = 2; +} + +// Section of statuses for session-related operations. +enum Session { + // [**4096**] Token not found. + TOKEN_NOT_FOUND = 0; + + // [**4097**] Token has expired. + TOKEN_EXPIRED = 1; +} + +// Section of status for APE manager related operations. +enum APEManager { + // [**5120**] The operation is denied by APE manager. + APE_MANAGER_ACCESS_DENIED = 0; +} \ No newline at end of file diff --git a/protosV2/src/main/proto/tombstone/types.proto b/protosV2/src/main/proto/tombstone/types.proto new file mode 100644 index 0000000..3e821c1 --- /dev/null +++ b/protosV2/src/main/proto/tombstone/types.proto @@ -0,0 +1,27 @@ +syntax = "proto3"; + +package neo.fs.v2.tombstone; + +option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/tombstone/grpc;tombstone"; +option java_package = "frostFS.tombstone"; + +import "refs/types.proto"; + +// Tombstone keeps record of deleted objects for a few epochs until they are +// purged from the NeoFS network. +message Tombstone { + // Last NeoFS epoch number of the tombstone lifetime. It's set by the + // tombstone creator depending on the current NeoFS network settings. A + // tombstone object must have the same expiration epoch value in + // `__SYSTEM__EXPIRATION_EPOCH` (`__NEOFS__EXPIRATION_EPOCH` is deprecated) + // attribute. Otherwise, the tombstone will be rejected by a storage node. + uint64 expiration_epoch = 1 [ json_name = "expirationEpoch" ]; + + // 16 byte UUID used to identify the split object hierarchy parts. Must be + // unique inside a container. All objects participating in the split must + // have the same `split_id` value. + bytes split_id = 2 [ json_name = "splitID" ]; + + // List of objects to be deleted. + repeated neo.fs.v2.refs.ObjectID members = 3 [ json_name = "members" ]; +} From dc0eef770c922b6b81b199afbfc14238cfb56cff Mon Sep 17 00:00:00 2001 From: Bruk Ori Date: Tue, 16 Jul 2024 15:00:17 +0300 Subject: [PATCH 04/38] [#1] Refactor client structure add session logic add network logic Signed-off-by: Ori Bruk --- README.md | 4 +- client/pom.xml | 15 -- .../main/java/info/FrostFS/sdk/Client.java | 36 ----- .../java/info/FrostFS/sdk/ClientSettings.java | 57 +++++++ .../java/info/FrostFS/sdk/GrpcClient.java | 81 ++-------- .../src/main/java/info/FrostFS/sdk/Main.java | 54 +++++++ .../java/info/FrostFS/sdk/RequestSigner.java | 27 +++- .../main/java/info/FrostFS/sdk/Verifier.java | 14 +- .../FrostFS/sdk/services/ContextAccessor.java | 19 +++ .../FrostFS/sdk/services/FrostFSClient.java | 134 ++++++++++++++++ .../FrostFS/sdk/services/NetmapClient.java | 14 ++ .../FrostFS/sdk/services/SessionClient.java | 8 + .../FrostFS/sdk/services/ToolsClient.java | 8 + .../sdk/services/impl/ContainerService.java | 48 +++--- .../sdk/services/impl/FrostFSClient.java | 85 ----------- .../sdk/services/impl/NetmapService.java | 139 +++++++++++++++-- .../sdk/services/impl/ObjectService.java | 54 +++---- .../sdk/services/impl/SessionService.java | 30 ++-- .../FrostFS/sdk/tools/ClientEnvironment.java | 60 ++++++++ .../FrostFS/sdk/tools/NetworkSettings.java | 143 ++++++++++++++++++ .../info/FrostFS/sdk/jdo/SessionToken.java | 19 +++ .../sdk/jdo/netmap/NetmapSnapshot.java | 21 +++ .../info/FrostFS/sdk/jdo/netmap/NodeInfo.java | 30 +++- .../FrostFS/sdk/mappers/SessionMapper.java | 31 ++++ .../mappers/netmap/NetmapSnapshotMapper.java | 18 +++ .../sdk/mappers/netmap/NodeInfoMapper.java | 21 ++- 26 files changed, 868 insertions(+), 302 deletions(-) delete mode 100644 client/src/main/java/info/FrostFS/sdk/Client.java create mode 100644 client/src/main/java/info/FrostFS/sdk/ClientSettings.java create mode 100644 client/src/main/java/info/FrostFS/sdk/Main.java create mode 100644 client/src/main/java/info/FrostFS/sdk/services/ContextAccessor.java create mode 100644 client/src/main/java/info/FrostFS/sdk/services/FrostFSClient.java create mode 100644 client/src/main/java/info/FrostFS/sdk/services/NetmapClient.java create mode 100644 client/src/main/java/info/FrostFS/sdk/services/SessionClient.java create mode 100644 client/src/main/java/info/FrostFS/sdk/services/ToolsClient.java delete mode 100644 client/src/main/java/info/FrostFS/sdk/services/impl/FrostFSClient.java create mode 100644 client/src/main/java/info/FrostFS/sdk/tools/ClientEnvironment.java create mode 100644 client/src/main/java/info/FrostFS/sdk/tools/NetworkSettings.java create mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/jdo/SessionToken.java create mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/jdo/netmap/NetmapSnapshot.java create mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/mappers/SessionMapper.java create mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/mappers/netmap/NetmapSnapshotMapper.java diff --git a/README.md b/README.md index 26697ca..2883006 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ import info.FrostFS.sdk.enums.BasicAcl; import info.FrostFS.sdk.jdo.Container; import info.FrostFS.sdk.jdo.netmap.PlacementPolicy; import info.FrostFS.sdk.jdo.netmap.Replica; -import info.FrostFS.sdk.services.impl.FrostFSClient; +import info.FrostFS.sdk.services.FrostFSClient; public class ContainerExample { @@ -58,7 +58,7 @@ import info.FrostFS.sdk.jdo.ContainerId; import info.FrostFS.sdk.jdo.ObjectAttribute; import info.FrostFS.sdk.jdo.ObjectFilter; import info.FrostFS.sdk.jdo.ObjectHeader; -import info.FrostFS.sdk.services.impl.FrostFSClient; +import info.FrostFS.sdk.services.FrostFSClient; import java.io.FileInputStream; import java.io.IOException; diff --git a/client/pom.xml b/client/pom.xml index f748035..b416ee4 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -23,26 +23,11 @@ cryptography 0.1.0 - - info.FrostFS.sdk - protosV2 - 0.1.0 - info.FrostFS.sdk modelsV2 0.1.0 - - org.apache.logging.log4j - log4j-api - 2.7 - - - org.apache.logging.log4j - log4j-slf4j-impl - 2.7 - \ No newline at end of file diff --git a/client/src/main/java/info/FrostFS/sdk/Client.java b/client/src/main/java/info/FrostFS/sdk/Client.java deleted file mode 100644 index 2d254b9..0000000 --- a/client/src/main/java/info/FrostFS/sdk/Client.java +++ /dev/null @@ -1,36 +0,0 @@ -package info.FrostFS.sdk; - -import info.FrostFS.sdk.jdo.OwnerId; -import info.FrostFS.sdk.jdo.Version; - -import static info.FrostFS.sdk.KeyExtension.getPrivateKeyFromWIF; -import static info.FrostFS.sdk.KeyExtension.loadPublicKey; - -public class Client { - private final OwnerId ownerId; - private final Version version = new Version(2, 13); - private final byte[] privateKey; - private final byte[] publicKey; - - public Client(String key) { - this.privateKey = getPrivateKeyFromWIF(key); - this.publicKey = loadPublicKey(privateKey); - this.ownerId = OwnerId.fromKey(publicKey); - } - - public OwnerId getOwnerId() { - return ownerId; - } - - public Version getVersion() { - return version; - } - - public byte[] getPrivateKey() { - return privateKey; - } - - public byte[] getPublicKey() { - return publicKey; - } -} diff --git a/client/src/main/java/info/FrostFS/sdk/ClientSettings.java b/client/src/main/java/info/FrostFS/sdk/ClientSettings.java new file mode 100644 index 0000000..3b09497 --- /dev/null +++ b/client/src/main/java/info/FrostFS/sdk/ClientSettings.java @@ -0,0 +1,57 @@ +package info.FrostFS.sdk; + +import io.grpc.ChannelCredentials; +import org.apache.commons.lang3.StringUtils; + +public class ClientSettings { + private static final String ERROR_TEMPLATE = "%s is required parameter."; + + public String key; + public String host; + public ChannelCredentials creds; + + public ClientSettings(String key, String host) { + this.key = key; + this.host = host; + } + + public ChannelCredentials getCreds() { + return creds; + } + + public void setCreds(ChannelCredentials creds) { + this.creds = creds; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public void validate() { + StringBuilder errorMessage = new StringBuilder(); + + if (StringUtils.isEmpty(key)) { + errorMessage.append(String.format(ERROR_TEMPLATE, "Key")).append(System.lineSeparator()); + } + + if (StringUtils.isEmpty(host)) { + errorMessage.append(String.format(ERROR_TEMPLATE, "Host")).append(System.lineSeparator()); + } + + if (errorMessage.length() != 0) { + throw new IllegalArgumentException(errorMessage.toString()); + } + } +} diff --git a/client/src/main/java/info/FrostFS/sdk/GrpcClient.java b/client/src/main/java/info/FrostFS/sdk/GrpcClient.java index 0b32277..9062017 100644 --- a/client/src/main/java/info/FrostFS/sdk/GrpcClient.java +++ b/client/src/main/java/info/FrostFS/sdk/GrpcClient.java @@ -1,89 +1,26 @@ package info.FrostFS.sdk; -import frostFS.container.ContainerServiceGrpc; -import frostFS.netmap.NetmapServiceGrpc; -import frostFS.object.ObjectServiceGrpc; -import frostFS.session.SessionServiceGrpc; import io.grpc.Channel; -import io.grpc.netty.GrpcSslContexts; +import io.grpc.ChannelCredentials; import io.grpc.netty.NettyChannelBuilder; -import io.netty.handler.ssl.util.InsecureTrustManagerFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import javax.net.ssl.SSLException; import java.net.URI; import java.net.URISyntaxException; +import static java.util.Objects.isNull; + public class GrpcClient { - private static final Logger log = LoggerFactory.getLogger(GrpcClient.class); - private final ContainerServiceGrpc.ContainerServiceBlockingStub containerServiceBlockingClient; - private final NetmapServiceGrpc.NetmapServiceBlockingStub netmapServiceBlockingClient; - private final ObjectServiceGrpc.ObjectServiceBlockingStub objectServiceBlockingClient; - private final ObjectServiceGrpc.ObjectServiceStub objectServiceClient; - private final SessionServiceGrpc.SessionServiceBlockingStub sessionServiceBlockingClient; - - public GrpcClient(String host) { - Channel channel = initGrpcChannel(host); - this.containerServiceBlockingClient = ContainerServiceGrpc.newBlockingStub(channel); - this.netmapServiceBlockingClient = NetmapServiceGrpc.newBlockingStub(channel); - this.objectServiceBlockingClient = ObjectServiceGrpc.newBlockingStub(channel); - this.objectServiceClient = ObjectServiceGrpc.newStub(channel); - this.sessionServiceBlockingClient = SessionServiceGrpc.newBlockingStub(channel); - } - - public static Channel initGrpcChannel(String host) { - URI uri; + public static Channel initGrpcChannel(String host, ChannelCredentials creds) { try { - uri = new URI(host); + URI uri = new URI(host); + var channelBuilder = isNull(creds) ? NettyChannelBuilder.forAddress(uri.getHost(), uri.getPort()) + : NettyChannelBuilder.forAddress(uri.getHost(), uri.getPort(), creds); + + return channelBuilder.usePlaintext().build(); } catch (URISyntaxException exp) { var message = String.format("Host %s has invalid format. Error: %s", host, exp.getMessage()); - log.error(message); throw new IllegalArgumentException(message); } - - var channelBuilder = NettyChannelBuilder.forAddress(uri.getHost(), uri.getPort()) - .usePlaintext(); - - switch (uri.getScheme()) { - case "https": - try { - channelBuilder.sslContext( - GrpcSslContexts.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build() - ); - } catch (SSLException e) { - throw new RuntimeException(e); - } - break; - case "http": - break; - default: - var message = String.format("Host %s has invalid URI scheme: %s", host, uri.getScheme()); - log.error(message); - throw new IllegalArgumentException(message); - } - - return channelBuilder.build(); - } - - public ContainerServiceGrpc.ContainerServiceBlockingStub getContainerServiceBlockingClient() { - return containerServiceBlockingClient; - } - - public NetmapServiceGrpc.NetmapServiceBlockingStub getNetmapServiceBlockingClient() { - return netmapServiceBlockingClient; - } - - public ObjectServiceGrpc.ObjectServiceBlockingStub getObjectServiceBlockingClient() { - return objectServiceBlockingClient; - } - - public ObjectServiceGrpc.ObjectServiceStub getObjectServiceClient() { - return objectServiceClient; - } - - public SessionServiceGrpc.SessionServiceBlockingStub getSessionServiceBlockingClient() { - return sessionServiceBlockingClient; } } diff --git a/client/src/main/java/info/FrostFS/sdk/Main.java b/client/src/main/java/info/FrostFS/sdk/Main.java new file mode 100644 index 0000000..d16f06e --- /dev/null +++ b/client/src/main/java/info/FrostFS/sdk/Main.java @@ -0,0 +1,54 @@ +package info.FrostFS.sdk; + +import info.FrostFS.sdk.enums.BasicAcl; +import info.FrostFS.sdk.enums.ObjectType; +import info.FrostFS.sdk.jdo.*; +import info.FrostFS.sdk.jdo.netmap.PlacementPolicy; +import info.FrostFS.sdk.jdo.netmap.Replica; +import info.FrostFS.sdk.services.FrostFSClient; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.util.List; + +public class Main { + public static void main(String[] args) throws Exception { + ClientSettings clientSettings = new ClientSettings("KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq", "http://localhost:8080"); + FrostFSClient frostFSClient = new FrostFSClient(clientSettings); +/* + var res2 = frostFSClient.searchObjectsAsync(new ContainerId("EQGx2QeYHJb53uRwYGzcQaW191sZpdNrjutk6veUSV2R")); +*/ + + ObjectFrostFs res3 = frostFSClient + .getObjectAsync(new ContainerId("EQGx2QeYHJb53uRwYGzcQaW191sZpdNrjutk6veUSV2R"), new ObjectId("85orCLKSu3X1jGiTFmwmTUsBU88RBARNwuRwrEy5pyww")); + + + var ttttt = frostFSClient.getNetworkSettingsAsync(); + Container container2 = frostFSClient.getContainerAsync(new ContainerId("EQGx2QeYHJb53uRwYGzcQaW191sZpdNrjutk6veUSV2R")); + + + var placementPolicy = new PlacementPolicy(true, new Replica[]{new Replica(1)}); + var containerId = frostFSClient.createContainerAsync(new Container(BasicAcl.PUBLIC_RW, placementPolicy)); + Thread.sleep(1000); + + FileInputStream file = null; + try { + file = new FileInputStream("/home/ori/Desktop/cat.jpg"); + var cat = new ObjectHeader(containerId, ObjectType.REGULAR, new ObjectAttribute[]{new ObjectAttribute("Filename", "cat.jpg")}); + frostFSClient.putObjectAsync(cat, file); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } + + var res = frostFSClient.searchObjectsAsync(containerId); + + + Container container = frostFSClient.getContainerAsync(containerId); + List containerIds = frostFSClient.listContainersAsync(); +/* + frostFSClient.deleteContainerAsync(containerId); +*/ + containerIds = frostFSClient.listContainersAsync(); + System.out.println(); + } +} \ No newline at end of file diff --git a/client/src/main/java/info/FrostFS/sdk/RequestSigner.java b/client/src/main/java/info/FrostFS/sdk/RequestSigner.java index 264d52a..962a421 100644 --- a/client/src/main/java/info/FrostFS/sdk/RequestSigner.java +++ b/client/src/main/java/info/FrostFS/sdk/RequestSigner.java @@ -62,21 +62,27 @@ public class RequestSigner { return signature; } - public static frostFS.refs.Types.SignatureRFC6979 signRFC6979(byte[] publicKey, byte[] privateKey, AbstractMessage message) { + public static frostFS.refs.Types.SignatureRFC6979 signRFC6979(byte[] publicKey, + byte[] privateKey, + AbstractMessage message) { return frostFS.refs.Types.SignatureRFC6979.newBuilder() .setKey(ByteString.copyFrom(publicKey)) .setSign(ByteString.copyFrom(signRFC6979(privateKey, message.toByteArray()))) .build(); } - public static frostFS.refs.Types.SignatureRFC6979 signRFC6979(byte[] publicKey, byte[] privateKey, ByteString data) { + public static frostFS.refs.Types.SignatureRFC6979 signRFC6979(byte[] publicKey, + byte[] privateKey, + ByteString data) { return frostFS.refs.Types.SignatureRFC6979.newBuilder() .setKey(ByteString.copyFrom(publicKey)) .setSign(ByteString.copyFrom(signRFC6979(privateKey, data.toByteArray()))) .build(); } - public static frostFS.refs.Types.Signature signMessagePart(byte[] publicKey, byte[] privateKey, AbstractMessage data) { + public static frostFS.refs.Types.Signature signMessagePart(byte[] publicKey, + byte[] privateKey, + AbstractMessage data) { var data2Sign = data.getSerializedSize() == 0 ? new byte[]{} : data.toByteArray(); return frostFS.refs.Types.Signature.newBuilder() @@ -101,13 +107,22 @@ public class RequestSigner { var verifyOrigin = (AbstractMessage) verify.getField(verify.getDescriptorForType().findFieldByName("origin")); if (verifyOrigin.getSerializedSize() == 0) { - verifyBuilder.setField(verifyBuilder.getDescriptorForType().findFieldByName("body_signature"), signMessagePart(publicKey, privateKey, body)); + verifyBuilder.setField( + verifyBuilder.getDescriptorForType().findFieldByName("body_signature"), + signMessagePart(publicKey, privateKey, body) + ); } else { verifyBuilder.setField(verifyBuilder.getDescriptorForType().findFieldByName("origin"), verifyOrigin); } - verifyBuilder.setField(verifyBuilder.getDescriptorForType().findFieldByName("meta_signature"), signMessagePart(publicKey, privateKey, meta)); - verifyBuilder.setField(verifyBuilder.getDescriptorForType().findFieldByName("origin_signature"), signMessagePart(publicKey, privateKey, verifyOrigin)); + verifyBuilder.setField( + verifyBuilder.getDescriptorForType().findFieldByName("meta_signature"), + signMessagePart(publicKey, privateKey, meta) + ); + verifyBuilder.setField( + verifyBuilder.getDescriptorForType().findFieldByName("origin_signature"), + signMessagePart(publicKey, privateKey, verifyOrigin) + ); request.setField(request.getDescriptorForType().findFieldByName("verify_header"), verifyBuilder.build()); } diff --git a/client/src/main/java/info/FrostFS/sdk/Verifier.java b/client/src/main/java/info/FrostFS/sdk/Verifier.java index 657be82..7a1b136 100644 --- a/client/src/main/java/info/FrostFS/sdk/Verifier.java +++ b/client/src/main/java/info/FrostFS/sdk/Verifier.java @@ -22,10 +22,12 @@ import static org.bouncycastle.crypto.util.DigestFactory.createSHA256; import static org.bouncycastle.util.BigIntegers.fromUnsignedByteArray; public class Verifier { - public static final int RFC6979_SIGNATURE_SIZE = 64; + public static final int RFC6979_SIG_SIZE = 64; public static boolean verifyRFC6979(Types.SignatureRFC6979 signature, AbstractMessage message) { - return verifyRFC6979(signature.getKey().toByteArray(), message.toByteArray(), signature.getSign().toByteArray()); + return verifyRFC6979( + signature.getKey().toByteArray(), message.toByteArray(), signature.getSign().toByteArray() + ); } public static boolean verifyRFC6979(byte[] publicKey, byte[] data, byte[] sig) { @@ -45,17 +47,17 @@ public class Verifier { } private static BigInteger[] decodeSignature(byte[] sig) { - if (sig.length != RFC6979_SIGNATURE_SIZE) { + if (sig.length != RFC6979_SIG_SIZE) { throw new IllegalArgumentException( String.format("Wrong signature size. Expected length=%s, actual=%s", - RFC6979_SIGNATURE_SIZE, sig.length) + RFC6979_SIG_SIZE, sig.length) ); } var rs = new BigInteger[2]; - rs[0] = fromUnsignedByteArray(Arrays.copyOfRange(sig, 0, (RFC6979_SIGNATURE_SIZE / 2) - 1)); - rs[1] = fromUnsignedByteArray(Arrays.copyOfRange(sig, RFC6979_SIGNATURE_SIZE / 2, RFC6979_SIGNATURE_SIZE - 1)); + rs[0] = fromUnsignedByteArray(Arrays.copyOfRange(sig, 0, (RFC6979_SIG_SIZE / 2) - 1)); + rs[1] = fromUnsignedByteArray(Arrays.copyOfRange(sig, RFC6979_SIG_SIZE / 2, RFC6979_SIG_SIZE - 1)); return rs; } diff --git a/client/src/main/java/info/FrostFS/sdk/services/ContextAccessor.java b/client/src/main/java/info/FrostFS/sdk/services/ContextAccessor.java new file mode 100644 index 0000000..6985310 --- /dev/null +++ b/client/src/main/java/info/FrostFS/sdk/services/ContextAccessor.java @@ -0,0 +1,19 @@ +package info.FrostFS.sdk.services; + +import info.FrostFS.sdk.tools.ClientEnvironment; + +public class ContextAccessor { + protected ClientEnvironment context; + + public ContextAccessor(ClientEnvironment context) { + this.context = context; + } + + public ClientEnvironment getContext() { + return context; + } + + public void setContext(ClientEnvironment context) { + this.context = context; + } +} diff --git a/client/src/main/java/info/FrostFS/sdk/services/FrostFSClient.java b/client/src/main/java/info/FrostFS/sdk/services/FrostFSClient.java new file mode 100644 index 0000000..44a0388 --- /dev/null +++ b/client/src/main/java/info/FrostFS/sdk/services/FrostFSClient.java @@ -0,0 +1,134 @@ +package info.FrostFS.sdk.services; + +import frostFS.session.Types; +import info.FrostFS.sdk.ClientSettings; +import info.FrostFS.sdk.jdo.*; +import info.FrostFS.sdk.jdo.netmap.NetmapSnapshot; +import info.FrostFS.sdk.jdo.netmap.NodeInfo; +import info.FrostFS.sdk.services.impl.ContainerService; +import info.FrostFS.sdk.services.impl.NetmapService; +import info.FrostFS.sdk.services.impl.ObjectService; +import info.FrostFS.sdk.services.impl.SessionService; +import info.FrostFS.sdk.tools.ClientEnvironment; +import info.FrostFS.sdk.tools.NetworkSettings; +import io.grpc.Channel; + +import java.io.FileInputStream; +import java.util.List; + +import static info.FrostFS.sdk.GrpcClient.initGrpcChannel; +import static java.util.Objects.isNull; + +public class FrostFSClient implements ContainerClient, ObjectClient, NetmapClient, SessionClient, ToolsClient { + private final ContainerService containerService; + private final NetmapService netmapService; + private final ObjectService objectService; + private final SessionService sessionService; + private final ClientEnvironment clientEnvironment; + + public FrostFSClient(ClientSettings clientSettings) { + if (isNull(clientSettings)) { + throw new IllegalArgumentException("Options must be initialized"); + } + + clientSettings.validate(); + Channel channel = initGrpcChannel(clientSettings.getHost(), clientSettings.getCreds()); + + this.clientEnvironment = + new ClientEnvironment(clientSettings.getKey(), channel, new Version(2, 13), this); + + this.containerService = new ContainerService(clientEnvironment); + this.netmapService = new NetmapService(clientEnvironment); + this.sessionService = new SessionService(clientEnvironment); + this.objectService = new ObjectService(clientEnvironment); + checkFrostFsVersionSupport(clientEnvironment.getVersion()); + } + + private void checkFrostFsVersionSupport(Version version) { + var localNodeInfo = netmapService.getLocalNodeInfoAsync(); + if (!localNodeInfo.getVersion().isSupported(version)) { + var msg = String.format("FrostFS %s is not supported.", localNodeInfo.getVersion()); + System.out.println(msg); + throw new IllegalArgumentException(msg); + } + } + + @Override + public Container getContainerAsync(ContainerId cid) { + return containerService.getContainerAsync(cid); + } + + @Override + public List listContainersAsync() { + return containerService.listContainersAsync(); + } + + @Override + public ContainerId createContainerAsync(Container container) { + return containerService.createContainerAsync(container); + } + + @Override + public void deleteContainerAsync(ContainerId cid) { + containerService.deleteContainerAsync(cid); + } + + @Override + public ObjectHeader getObjectHeadAsync(ContainerId containerId, ObjectId objectId) { + return objectService.getObjectHeadAsync(containerId, objectId); + } + + @Override + public ObjectFrostFs getObjectAsync(ContainerId containerId, ObjectId objectId) { + return objectService.getObjectAsync(containerId, objectId); + } + + @Override + public ObjectId putObjectAsync(ObjectHeader header, FileInputStream payload) { + return objectService.putObjectAsync(header, payload); + } + + @Override + public ObjectId putObjectAsync(ObjectHeader header, byte[] payload) { + return objectService.putObjectAsync(header, payload); + } + + @Override + public void deleteObjectAsync(ContainerId containerId, ObjectId objectId) { + objectService.deleteObjectAsync(containerId, objectId); + } + + @Override + public Iterable searchObjectsAsync(ContainerId cid, ObjectFilter... filters) { + return objectService.searchObjectsAsync(cid, filters); + } + + @Override + public NetmapSnapshot getNetmapSnapshotAsync() { + return netmapService.getNetmapSnapshotAsync(); + } + + @Override + public NodeInfo getLocalNodeInfoAsync() { + return netmapService.getLocalNodeInfoAsync(); + } + + @Override + public NetworkSettings getNetworkSettingsAsync() { + return netmapService.getNetworkSettingsAsync(); + } + + @Override + public SessionToken createSessionAsync(long expiration) { + return sessionService.createSessionAsync(expiration); + } + + public Types.SessionToken createSessionInternalAsync(long expiration) { + return sessionService.createSessionInternalAsync(expiration); + } + + @Override + public ObjectId CalculateObjectId(ObjectHeader header) { + return null; + } +} diff --git a/client/src/main/java/info/FrostFS/sdk/services/NetmapClient.java b/client/src/main/java/info/FrostFS/sdk/services/NetmapClient.java new file mode 100644 index 0000000..f6e9401 --- /dev/null +++ b/client/src/main/java/info/FrostFS/sdk/services/NetmapClient.java @@ -0,0 +1,14 @@ +package info.FrostFS.sdk.services; + +import info.FrostFS.sdk.jdo.netmap.NetmapSnapshot; +import info.FrostFS.sdk.jdo.netmap.NodeInfo; +import info.FrostFS.sdk.tools.NetworkSettings; + +public interface NetmapClient { + + NetmapSnapshot getNetmapSnapshotAsync(); + + NodeInfo getLocalNodeInfoAsync(); + + NetworkSettings getNetworkSettingsAsync(); +} diff --git a/client/src/main/java/info/FrostFS/sdk/services/SessionClient.java b/client/src/main/java/info/FrostFS/sdk/services/SessionClient.java new file mode 100644 index 0000000..ffd1f0a --- /dev/null +++ b/client/src/main/java/info/FrostFS/sdk/services/SessionClient.java @@ -0,0 +1,8 @@ +package info.FrostFS.sdk.services; + +import info.FrostFS.sdk.jdo.SessionToken; + +public interface SessionClient { + + SessionToken createSessionAsync(long expiration); +} diff --git a/client/src/main/java/info/FrostFS/sdk/services/ToolsClient.java b/client/src/main/java/info/FrostFS/sdk/services/ToolsClient.java new file mode 100644 index 0000000..62dffc2 --- /dev/null +++ b/client/src/main/java/info/FrostFS/sdk/services/ToolsClient.java @@ -0,0 +1,8 @@ +package info.FrostFS.sdk.services; + +import info.FrostFS.sdk.jdo.ObjectHeader; +import info.FrostFS.sdk.jdo.ObjectId; + +public interface ToolsClient { + ObjectId CalculateObjectId(ObjectHeader header); +} diff --git a/client/src/main/java/info/FrostFS/sdk/services/impl/ContainerService.java b/client/src/main/java/info/FrostFS/sdk/services/impl/ContainerService.java index 782c218..824f967 100644 --- a/client/src/main/java/info/FrostFS/sdk/services/impl/ContainerService.java +++ b/client/src/main/java/info/FrostFS/sdk/services/impl/ContainerService.java @@ -2,7 +2,6 @@ package info.FrostFS.sdk.services.impl; import frostFS.container.ContainerServiceGrpc; import frostFS.container.Service; -import info.FrostFS.sdk.Client; import info.FrostFS.sdk.Verifier; import info.FrostFS.sdk.jdo.Container; import info.FrostFS.sdk.jdo.ContainerId; @@ -11,6 +10,8 @@ import info.FrostFS.sdk.mappers.ContainerMapper; import info.FrostFS.sdk.mappers.OwnerIdMapper; import info.FrostFS.sdk.mappers.VersionMapper; import info.FrostFS.sdk.services.ContainerClient; +import info.FrostFS.sdk.services.ContextAccessor; +import info.FrostFS.sdk.tools.ClientEnvironment; import java.util.List; import java.util.stream.Collectors; @@ -19,23 +20,24 @@ import static info.FrostFS.sdk.RequestConstructor.addMetaHeader; import static info.FrostFS.sdk.RequestSigner.sign; import static info.FrostFS.sdk.RequestSigner.signRFC6979; -public class ContainerService implements ContainerClient { +public class ContainerService extends ContextAccessor implements ContainerClient { private final ContainerServiceGrpc.ContainerServiceBlockingStub containerServiceAsyncClient; - private final Client client; - public ContainerService(ContainerServiceGrpc.ContainerServiceBlockingStub containerServiceAsyncClient, Client client) { - this.containerServiceAsyncClient = containerServiceAsyncClient; - this.client = client; + public ContainerService(ClientEnvironment clientEnvironment) { + super(clientEnvironment); + this.containerServiceAsyncClient = ContainerServiceGrpc.newBlockingStub(clientEnvironment.getChannel()); } public Container getContainerAsync(ContainerId cid) { var request = Service.GetRequest.newBuilder() .setBody( - Service.GetRequest.Body.newBuilder().setContainerId(ContainerIdMapper.toGrpcMessage(cid)).build() + Service.GetRequest.Body.newBuilder() + .setContainerId(ContainerIdMapper.toGrpcMessage(cid)) + .build() ); - addMetaHeader(request, null); - sign(request, client.getPublicKey(), client.getPrivateKey()); + addMetaHeader(request); + sign(request, context.getPublicKey(), context.getPrivateKey()); var response = containerServiceAsyncClient.get(request.build()); @@ -46,11 +48,13 @@ public class ContainerService implements ContainerClient { public List listContainersAsync() { var request = Service.ListRequest.newBuilder() .setBody( - Service.ListRequest.Body.newBuilder().setOwnerId(OwnerIdMapper.toGrpcMessage(client.getOwnerId())).build() + Service.ListRequest.Body.newBuilder() + .setOwnerId(OwnerIdMapper.toGrpcMessage(context.getOwnerId())) + .build() ); - addMetaHeader(request, null); - sign(request, client.getPublicKey(), client.getPrivateKey()); + addMetaHeader(request); + sign(request, context.getPublicKey(), context.getPrivateKey()); var response = containerServiceAsyncClient.list(request.build()); @@ -65,20 +69,22 @@ public class ContainerService implements ContainerClient { var grpcContainer = ContainerMapper.toGrpcMessage(container); grpcContainer = grpcContainer.toBuilder() - .setOwnerId(OwnerIdMapper.toGrpcMessage(client.getOwnerId())) - .setVersion(VersionMapper.toGrpcMessage(client.getVersion())) + .setOwnerId(OwnerIdMapper.toGrpcMessage(context.getOwnerId())) + .setVersion(VersionMapper.toGrpcMessage(context.getVersion())) .build(); var request = Service.PutRequest.newBuilder() .setBody( Service.PutRequest.Body.newBuilder() .setContainer(grpcContainer) - .setSignature(signRFC6979(client.getPublicKey(), client.getPrivateKey(), grpcContainer)) + .setSignature( + signRFC6979(context.getPublicKey(), context.getPrivateKey(), grpcContainer) + ) .build() ); - addMetaHeader(request, null); - sign(request, client.getPublicKey(), client.getPrivateKey()); + addMetaHeader(request); + sign(request, context.getPublicKey(), context.getPrivateKey()); var response = containerServiceAsyncClient.put(request.build()); @@ -93,12 +99,14 @@ public class ContainerService implements ContainerClient { .setBody( Service.DeleteRequest.Body.newBuilder() .setContainerId(grpcContainerId) - .setSignature(signRFC6979(client.getPublicKey(), client.getPrivateKey(), grpcContainerId.getValue())) + .setSignature(signRFC6979( + context.getPublicKey(), context.getPrivateKey(), grpcContainerId.getValue() + )) .build() ); - addMetaHeader(request, null); - sign(request, client.getPublicKey(), client.getPrivateKey()); + addMetaHeader(request); + sign(request, context.getPublicKey(), context.getPrivateKey()); var response = containerServiceAsyncClient.delete(request.build()); diff --git a/client/src/main/java/info/FrostFS/sdk/services/impl/FrostFSClient.java b/client/src/main/java/info/FrostFS/sdk/services/impl/FrostFSClient.java deleted file mode 100644 index a720d4c..0000000 --- a/client/src/main/java/info/FrostFS/sdk/services/impl/FrostFSClient.java +++ /dev/null @@ -1,85 +0,0 @@ -package info.FrostFS.sdk.services.impl; - -import info.FrostFS.sdk.Client; -import info.FrostFS.sdk.GrpcClient; -import info.FrostFS.sdk.jdo.*; -import info.FrostFS.sdk.services.ContainerClient; -import info.FrostFS.sdk.services.ObjectClient; - -import java.io.FileInputStream; -import java.util.List; - -public class FrostFSClient implements ContainerClient, ObjectClient { - private final ContainerService containerService; - private final NetmapService netmapService; - private final ObjectService objectService; - - public FrostFSClient(GrpcClient grpcClient, Client client) { - this.containerService = new ContainerService(grpcClient.getContainerServiceBlockingClient(), client); - this.netmapService = new NetmapService(grpcClient.getNetmapServiceBlockingClient(), client); - SessionService sessionService = new SessionService(grpcClient.getSessionServiceBlockingClient(), client); - this.objectService = new ObjectService( - grpcClient.getObjectServiceBlockingClient(), grpcClient.getObjectServiceClient(), sessionService, client - ); - checkFrostFsVersionSupport(client); - } - - private void checkFrostFsVersionSupport(Client client) { - var localNodeInfo = netmapService.getLocalNodeInfoAsync(); - if (!localNodeInfo.getVersion().isSupported(client.getVersion())) { - var msg = String.format("FrostFS %s is not supported.", localNodeInfo.getVersion()); - System.out.println(msg); - throw new IllegalArgumentException(msg); - } - } - - @Override - public Container getContainerAsync(ContainerId cid) { - return containerService.getContainerAsync(cid); - } - - @Override - public List listContainersAsync() { - return containerService.listContainersAsync(); - } - - @Override - public ContainerId createContainerAsync(Container container) { - return containerService.createContainerAsync(container); - } - - @Override - public void deleteContainerAsync(ContainerId cid) { - containerService.deleteContainerAsync(cid); - } - - @Override - public ObjectHeader getObjectHeadAsync(ContainerId containerId, ObjectId objectId) { - return objectService.getObjectHeadAsync(containerId, objectId); - } - - @Override - public ObjectFrostFs getObjectAsync(ContainerId containerId, ObjectId objectId) { - return objectService.getObjectAsync(containerId, objectId); - } - - @Override - public ObjectId putObjectAsync(ObjectHeader header, FileInputStream payload) { - return objectService.putObjectAsync(header, payload); - } - - @Override - public ObjectId putObjectAsync(ObjectHeader header, byte[] payload) { - return objectService.putObjectAsync(header, payload); - } - - @Override - public void deleteObjectAsync(ContainerId containerId, ObjectId objectId) { - objectService.deleteObjectAsync(containerId, objectId); - } - - @Override - public Iterable searchObjectsAsync(ContainerId cid, ObjectFilter... filters) { - return objectService.searchObjectsAsync(cid, filters); - } -} diff --git a/client/src/main/java/info/FrostFS/sdk/services/impl/NetmapService.java b/client/src/main/java/info/FrostFS/sdk/services/impl/NetmapService.java index eafc40f..b2ccf23 100644 --- a/client/src/main/java/info/FrostFS/sdk/services/impl/NetmapService.java +++ b/client/src/main/java/info/FrostFS/sdk/services/impl/NetmapService.java @@ -2,30 +2,124 @@ package info.FrostFS.sdk.services.impl; import frostFS.netmap.NetmapServiceGrpc; import frostFS.netmap.Service; -import info.FrostFS.sdk.Client; +import frostFS.netmap.Types; +import info.FrostFS.sdk.Verifier; +import info.FrostFS.sdk.jdo.netmap.NetmapSnapshot; import info.FrostFS.sdk.jdo.netmap.NodeInfo; +import info.FrostFS.sdk.mappers.netmap.NetmapSnapshotMapper; import info.FrostFS.sdk.mappers.netmap.NodeInfoMapper; +import info.FrostFS.sdk.services.ContextAccessor; +import info.FrostFS.sdk.services.NetmapClient; +import info.FrostFS.sdk.tools.ClientEnvironment; +import info.FrostFS.sdk.tools.NetworkSettings; + +import java.nio.charset.StandardCharsets; import static info.FrostFS.sdk.RequestConstructor.addMetaHeader; import static info.FrostFS.sdk.RequestSigner.sign; +import static java.util.Objects.nonNull; -public class NetmapService { - private final NetmapServiceGrpc.NetmapServiceBlockingStub netmapServiceAsyncClient; - private final Client client; +public class NetmapService extends ContextAccessor implements NetmapClient { + private final NetmapServiceGrpc.NetmapServiceBlockingStub netmapServiceClient; - public NetmapService(NetmapServiceGrpc.NetmapServiceBlockingStub netmapServiceAsyncClient, Client client) { - this.netmapServiceAsyncClient = netmapServiceAsyncClient; - this.client = client; + public NetmapService(ClientEnvironment clientEnvironment) { + super(clientEnvironment); + this.netmapServiceClient = NetmapServiceGrpc.newBlockingStub(context.getChannel()); } + private static boolean getBoolValue(byte[] bytes) { + for (var byteValue : bytes) { + if (byteValue != 0) return true; + } + + return false; + } + + private static long getLongValue(byte[] bytes) { + long val = 0; + for (var i = bytes.length - 1; i >= 0; i--) { + val = (val << 8) + bytes[i]; + } + + return val; + } + + private static void setNetworksParam(Types.NetworkConfig.Parameter param, NetworkSettings settings) { + var key = new String(param.getKey().toByteArray(), StandardCharsets.UTF_8); + + var valueBytes = param.getValue().toByteArray(); + switch (key) { + case "AuditFee": + settings.setAuditFee(getLongValue(valueBytes)); + break; + case "BasicIncomeRate": + settings.setBasicIncomeRate(getLongValue(valueBytes)); + break; + case "ContainerFee": + settings.setContainerFee(getLongValue(valueBytes)); + break; + case "ContainerAliasFee": + settings.setContainerAliasFee(getLongValue(valueBytes)); + break; + case "EpochDuration": + settings.setEpochDuration(getLongValue(valueBytes)); + break; + case "InnerRingCandidateFee": + settings.setiRCandidateFee(getLongValue(valueBytes)); + break; + case "MaxECDataCount": + settings.setMaxECDataCount(getLongValue(valueBytes)); + break; + case "MaxECParityCount": + settings.setMaxECParityCount(getLongValue(valueBytes)); + break; + case "MaxObjectSize": + settings.setMaxObjectSize(getLongValue(valueBytes)); + break; + case "WithdrawFee": + settings.setWithdrawalFee(getLongValue(valueBytes)); + break; + case "HomomorphicHashingDisabled": + settings.setHomomorphicHashingDisabled(getBoolValue(valueBytes)); + break; + case "MaintenanceModeAllowed": + settings.setMaintenanceModeAllowed(getBoolValue(valueBytes)); + break; + default: + settings.getUnnamedSettings().put(key, valueBytes); + break; + } + } + + @Override + public NetworkSettings getNetworkSettingsAsync() { + if (nonNull(context.getNetworkSettings())) { + return context.getNetworkSettings(); + } + + var info = getNetworkInfoAsync(); + + var settings = new NetworkSettings(); + + for (var param : info.getBody().getNetworkInfo().getNetworkConfig().getParametersList()) { + setNetworksParam(param, settings); + } + + context.setNetworkSettings(settings); + + return settings; + } + + @Override public NodeInfo getLocalNodeInfoAsync() { var request = Service.LocalNodeInfoRequest.newBuilder() .setBody(Service.LocalNodeInfoRequest.Body.newBuilder().build()); - addMetaHeader(request, null); - sign(request, client.getPublicKey(), client.getPrivateKey()); + addMetaHeader(request); + sign(request, context.getPublicKey(), context.getPrivateKey()); - var response = netmapServiceAsyncClient.localNodeInfo(request.build()); + var response = netmapServiceClient.localNodeInfo(request.build()); + Verifier.checkResponse(response); return NodeInfoMapper.toModel(response.getBody()); } @@ -34,9 +128,28 @@ public class NetmapService { var request = Service.NetworkInfoRequest.newBuilder() .setBody(Service.NetworkInfoRequest.Body.newBuilder().build()); - addMetaHeader(request, null); - sign(request, client.getPublicKey(), client.getPrivateKey()); + addMetaHeader(request); + sign(request, context.getPublicKey(), context.getPrivateKey()); - return netmapServiceAsyncClient.networkInfo(request.build()); + var response = netmapServiceClient.networkInfo(request.build()); + + Verifier.checkResponse(response); + + return response; + } + + @Override + public NetmapSnapshot getNetmapSnapshotAsync() { + var request = Service.NetmapSnapshotRequest.newBuilder() + .setBody(Service.NetmapSnapshotRequest.Body.newBuilder().build()); + + addMetaHeader(request); + sign(request, context.getPublicKey(), context.getPrivateKey()); + + var response = netmapServiceClient.netmapSnapshot(request.build()); + + Verifier.checkResponse(response); + + return NetmapSnapshotMapper.toModel(response); } } diff --git a/client/src/main/java/info/FrostFS/sdk/services/impl/ObjectService.java b/client/src/main/java/info/FrostFS/sdk/services/impl/ObjectService.java index f90fe84..9ef6a6d 100644 --- a/client/src/main/java/info/FrostFS/sdk/services/impl/ObjectService.java +++ b/client/src/main/java/info/FrostFS/sdk/services/impl/ObjectService.java @@ -5,11 +5,12 @@ import com.google.protobuf.ByteString; import frostFS.object.ObjectServiceGrpc; import frostFS.object.Service; import frostFS.refs.Types; -import info.FrostFS.sdk.Client; import info.FrostFS.sdk.Verifier; import info.FrostFS.sdk.jdo.*; import info.FrostFS.sdk.mappers.*; +import info.FrostFS.sdk.services.ContextAccessor; import info.FrostFS.sdk.services.ObjectClient; +import info.FrostFS.sdk.tools.ClientEnvironment; import java.io.ByteArrayInputStream; import java.io.FileInputStream; @@ -25,21 +26,14 @@ import static info.FrostFS.sdk.RequestConstructor.addObjectSessionToken; import static info.FrostFS.sdk.RequestSigner.sign; import static java.util.Objects.nonNull; -public class ObjectService implements ObjectClient { +public class ObjectService extends ContextAccessor implements ObjectClient { private final ObjectServiceGrpc.ObjectServiceBlockingStub objectServiceBlockingClient; private final ObjectServiceGrpc.ObjectServiceStub objectServiceClient; - private final SessionService sessionService; - private final Client client; - - public ObjectService(ObjectServiceGrpc.ObjectServiceBlockingStub objectServiceAsyncClient, - ObjectServiceGrpc.ObjectServiceStub objectServiceClient, - SessionService sessionService, - Client client) { - this.objectServiceBlockingClient = objectServiceAsyncClient; - this.objectServiceClient = objectServiceClient; - this.client = client; - this.sessionService = sessionService; + public ObjectService(ClientEnvironment clientEnvironment) { + super(clientEnvironment); + this.objectServiceBlockingClient = ObjectServiceGrpc.newBlockingStub(context.getChannel()); + this.objectServiceClient = ObjectServiceGrpc.newStub(context.getChannel()); } @Override @@ -55,8 +49,8 @@ public class ObjectService implements ObjectClient { ).build() ); - addMetaHeader(request, null); - sign(request, client.getPublicKey(), client.getPrivateKey()); + addMetaHeader(request); + sign(request, context.getPublicKey(), context.getPrivateKey()); var response = objectServiceBlockingClient.head(request.build()); Verifier.checkResponse(response); @@ -66,7 +60,7 @@ public class ObjectService implements ObjectClient { @Override public ObjectFrostFs getObjectAsync(ContainerId cid, ObjectId oid) { - var sessionToken = sessionService.createSessionAsync(-1); + var sessionToken = context.getFrostFSClient().createSessionInternalAsync(-1); var request = Service.GetRequest.newBuilder() .setBody( @@ -81,12 +75,12 @@ public class ObjectService implements ObjectClient { .build() ); - addMetaHeader(request, null); + addMetaHeader(request); addObjectSessionToken( request, sessionToken, ContainerIdMapper.toGrpcMessage(cid), ObjectIdMapper.toGrpcMessage(oid), - frostFS.session.Types.ObjectSessionContext.Verb.GET, client.getPublicKey(), client.getPrivateKey() + frostFS.session.Types.ObjectSessionContext.Verb.GET, context.getPublicKey(), context.getPrivateKey() ); - sign(request, client.getPublicKey(), client.getPrivateKey()); + sign(request, context.getPublicKey(), context.getPrivateKey()); var obj = getObject(request.build()); @@ -106,8 +100,8 @@ public class ObjectService implements ObjectClient { ) .build()); - addMetaHeader(request, null); - sign(request, client.getPublicKey(), client.getPrivateKey()); + addMetaHeader(request); + sign(request, context.getPublicKey(), context.getPrivateKey()); var response = objectServiceBlockingClient.delete(request.build()); Verifier.checkResponse(response); @@ -136,8 +130,8 @@ public class ObjectService implements ObjectClient { var request = Service.SearchRequest.newBuilder() .setBody(body.build()); - addMetaHeader(request, null); - sign(request, client.getPublicKey(), client.getPrivateKey()); + addMetaHeader(request); + sign(request, context.getPublicKey(), context.getPrivateKey()); var objectsIds = searchObjects(request.build()); @@ -169,12 +163,12 @@ public class ObjectService implements ObjectClient { } private ObjectId putObject(ObjectHeader header, InputStream payload) { - var sessionToken = sessionService.createSessionAsync(-1); + var sessionToken = context.getFrostFSClient().createSessionInternalAsync(-1); var hdr = ObjectHeaderMapper.toGrpcMessage(header); hdr = hdr.toBuilder() - .setOwnerId(OwnerIdMapper.toGrpcMessage(client.getOwnerId())) - .setVersion(VersionMapper.toGrpcMessage(client.getVersion())) + .setOwnerId(OwnerIdMapper.toGrpcMessage(context.getOwnerId())) + .setVersion(VersionMapper.toGrpcMessage(context.getVersion())) .build(); var oid = Types.ObjectID.newBuilder().setValue(getSha256(hdr)).build(); @@ -187,12 +181,12 @@ public class ObjectService implements ObjectClient { ).build() ); - addMetaHeader(request, null); + addMetaHeader(request); addObjectSessionToken( request, sessionToken, hdr.getContainerId(), oid, frostFS.session.Types.ObjectSessionContext.Verb.PUT, - client.getPublicKey(), client.getPrivateKey() + context.getPublicKey(), context.getPrivateKey() ); - sign(request, client.getPublicKey(), client.getPrivateKey()); + sign(request, context.getPublicKey(), context.getPrivateKey()); var writer = putObjectInit(request.build()); @@ -208,7 +202,7 @@ public class ObjectService implements ObjectClient { .build() ) .clearVerifyHeader(); - sign(request, client.getPublicKey(), client.getPrivateKey()); + sign(request, context.getPublicKey(), context.getPrivateKey()); writer.write(request.build()); bufferLength = payload.readNBytes(buffer, 0, Constants.OBJECT_CHUNK_SIZE); } diff --git a/client/src/main/java/info/FrostFS/sdk/services/impl/SessionService.java b/client/src/main/java/info/FrostFS/sdk/services/impl/SessionService.java index 832dc52..3406acf 100644 --- a/client/src/main/java/info/FrostFS/sdk/services/impl/SessionService.java +++ b/client/src/main/java/info/FrostFS/sdk/services/impl/SessionService.java @@ -3,31 +3,41 @@ package info.FrostFS.sdk.services.impl; import frostFS.session.Service; import frostFS.session.SessionServiceGrpc; import frostFS.session.Types; -import info.FrostFS.sdk.Client; +import info.FrostFS.sdk.jdo.SessionToken; import info.FrostFS.sdk.mappers.OwnerIdMapper; +import info.FrostFS.sdk.mappers.SessionMapper; +import info.FrostFS.sdk.services.ContextAccessor; +import info.FrostFS.sdk.services.SessionClient; +import info.FrostFS.sdk.tools.ClientEnvironment; import static info.FrostFS.sdk.RequestConstructor.addMetaHeader; import static info.FrostFS.sdk.RequestSigner.sign; -public class SessionService { +public class SessionService extends ContextAccessor implements SessionClient { private final SessionServiceGrpc.SessionServiceBlockingStub sessionServiceAsyncClient; - private final Client client; - public SessionService(SessionServiceGrpc.SessionServiceBlockingStub sessionServiceAsyncClient, Client client) { - this.sessionServiceAsyncClient = sessionServiceAsyncClient; - this.client = client; + public SessionService(ClientEnvironment clientEnvironment) { + super(clientEnvironment); + this.sessionServiceAsyncClient = SessionServiceGrpc.newBlockingStub(context.getChannel()); } - protected Types.SessionToken createSessionAsync(long expiration) { + @Override + public SessionToken createSessionAsync(long expiration) { + var sessionToken = createSessionInternalAsync(expiration); + var token = SessionMapper.serialize(sessionToken); + return new SessionToken(new byte[]{}, token); + } + + public Types.SessionToken createSessionInternalAsync(long expiration) { var request = Service.CreateRequest.newBuilder() .setBody( Service.CreateRequest.Body.newBuilder() - .setOwnerId(OwnerIdMapper.toGrpcMessage(client.getOwnerId())) + .setOwnerId(OwnerIdMapper.toGrpcMessage(context.getOwnerId())) .setExpiration(expiration).build() ); - addMetaHeader(request, null); - sign(request, client.getPublicKey(), client.getPrivateKey()); + addMetaHeader(request); + sign(request, context.getPublicKey(), context.getPrivateKey()); return createSession(request.build()); } diff --git a/client/src/main/java/info/FrostFS/sdk/tools/ClientEnvironment.java b/client/src/main/java/info/FrostFS/sdk/tools/ClientEnvironment.java new file mode 100644 index 0000000..93019ca --- /dev/null +++ b/client/src/main/java/info/FrostFS/sdk/tools/ClientEnvironment.java @@ -0,0 +1,60 @@ +package info.FrostFS.sdk.tools; + +import info.FrostFS.sdk.jdo.OwnerId; +import info.FrostFS.sdk.jdo.Version; +import info.FrostFS.sdk.services.FrostFSClient; +import io.grpc.Channel; + +import static info.FrostFS.sdk.KeyExtension.getPrivateKeyFromWIF; +import static info.FrostFS.sdk.KeyExtension.loadPublicKey; + +public class ClientEnvironment { + private final OwnerId ownerId; + private final Version version; + private final byte[] privateKey; + private final byte[] publicKey; + private final Channel channel; + private final FrostFSClient frostFSClient; + private NetworkSettings networkSettings; + + public ClientEnvironment(String key, Channel channel, Version version, FrostFSClient frostFSClient) { + this.privateKey = getPrivateKeyFromWIF(key); + this.publicKey = loadPublicKey(privateKey); + this.ownerId = OwnerId.fromKey(publicKey); + this.version = version; + this.channel = channel; + this.frostFSClient = frostFSClient; + } + + public Channel getChannel() { + return channel; + } + + public NetworkSettings getNetworkSettings() { + return networkSettings; + } + + public void setNetworkSettings(NetworkSettings networkSettings) { + this.networkSettings = networkSettings; + } + + public FrostFSClient getFrostFSClient() { + return frostFSClient; + } + + public OwnerId getOwnerId() { + return ownerId; + } + + public Version getVersion() { + return version; + } + + public byte[] getPrivateKey() { + return privateKey; + } + + public byte[] getPublicKey() { + return publicKey; + } +} diff --git a/client/src/main/java/info/FrostFS/sdk/tools/NetworkSettings.java b/client/src/main/java/info/FrostFS/sdk/tools/NetworkSettings.java new file mode 100644 index 0000000..d6971b3 --- /dev/null +++ b/client/src/main/java/info/FrostFS/sdk/tools/NetworkSettings.java @@ -0,0 +1,143 @@ +package info.FrostFS.sdk.tools; + +import java.util.HashMap; +import java.util.Map; + +public class NetworkSettings { + + public Long auditFee; + public Long basicIncomeRate; + public Long containerFee; + public Long containerAliasFee; + public Long innerRingCandidateFee; + public Long withdrawFee; + public Long epochDuration; + public Long iRCandidateFee; + public Long maxObjectSize; + public Long maxECDataCount; + public Long maxECParityCount; + public Long withdrawalFee; + public Boolean homomorphicHashingDisabled; + public Boolean maintenanceModeAllowed; + public Map unnamedSettings = new HashMap<>(); + + public Long getAuditFee() { + return auditFee; + } + + public void setAuditFee(Long auditFee) { + this.auditFee = auditFee; + } + + public Long getBasicIncomeRate() { + return basicIncomeRate; + } + + public void setBasicIncomeRate(Long basicIncomeRate) { + this.basicIncomeRate = basicIncomeRate; + } + + public long getContainerFee() { + return containerFee; + } + + public void setContainerFee(long containerFee) { + this.containerFee = containerFee; + } + + public long getContainerAliasFee() { + return containerAliasFee; + } + + public void setContainerAliasFee(long containerAliasFee) { + this.containerAliasFee = containerAliasFee; + } + + public long getInnerRingCandidateFee() { + return innerRingCandidateFee; + } + + public void setInnerRingCandidateFee(long innerRingCandidateFee) { + this.innerRingCandidateFee = innerRingCandidateFee; + } + + public long getWithdrawFee() { + return withdrawFee; + } + + public void setWithdrawFee(long withdrawFee) { + this.withdrawFee = withdrawFee; + } + + public long getEpochDuration() { + return epochDuration; + } + + public void setEpochDuration(long epochDuration) { + this.epochDuration = epochDuration; + } + + public long getiRCandidateFee() { + return iRCandidateFee; + } + + public void setiRCandidateFee(long iRCandidateFee) { + this.iRCandidateFee = iRCandidateFee; + } + + public long getMaxObjectSize() { + return maxObjectSize; + } + + public void setMaxObjectSize(long maxObjectSize) { + this.maxObjectSize = maxObjectSize; + } + + public long getMaxECDataCount() { + return maxECDataCount; + } + + public void setMaxECDataCount(long maxECDataCount) { + this.maxECDataCount = maxECDataCount; + } + + public long getMaxECParityCount() { + return maxECParityCount; + } + + public void setMaxECParityCount(long maxECParityCount) { + this.maxECParityCount = maxECParityCount; + } + + public long getWithdrawalFee() { + return withdrawalFee; + } + + public void setWithdrawalFee(long withdrawalFee) { + this.withdrawalFee = withdrawalFee; + } + + public boolean isHomomorphicHashingDisabled() { + return homomorphicHashingDisabled; + } + + public void setHomomorphicHashingDisabled(boolean homomorphicHashingDisabled) { + this.homomorphicHashingDisabled = homomorphicHashingDisabled; + } + + public boolean isMaintenanceModeAllowed() { + return maintenanceModeAllowed; + } + + public void setMaintenanceModeAllowed(boolean maintenanceModeAllowed) { + this.maintenanceModeAllowed = maintenanceModeAllowed; + } + + public Map getUnnamedSettings() { + return unnamedSettings; + } + + public void setUnnamedSettings(Map unnamedSettings) { + this.unnamedSettings = unnamedSettings; + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/SessionToken.java b/modelsV2/src/main/java/info/FrostFS/sdk/jdo/SessionToken.java new file mode 100644 index 0000000..bd4f7b0 --- /dev/null +++ b/modelsV2/src/main/java/info/FrostFS/sdk/jdo/SessionToken.java @@ -0,0 +1,19 @@ +package info.FrostFS.sdk.jdo; + +public class SessionToken { + public byte[] id; + public byte[] sessionKey; + + public SessionToken(byte[] id, byte[] sessionKey) { + this.id = id; + this.sessionKey = sessionKey; + } + + public byte[] getId() { + return id; + } + + public byte[] getSessionKey() { + return sessionKey; + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/netmap/NetmapSnapshot.java b/modelsV2/src/main/java/info/FrostFS/sdk/jdo/netmap/NetmapSnapshot.java new file mode 100644 index 0000000..7fd344e --- /dev/null +++ b/modelsV2/src/main/java/info/FrostFS/sdk/jdo/netmap/NetmapSnapshot.java @@ -0,0 +1,21 @@ +package info.FrostFS.sdk.jdo.netmap; + +import java.util.List; + +public class NetmapSnapshot { + public Long epoch; + public List nodeInfoCollection; + + public NetmapSnapshot(Long epoch, List nodeInfoCollection) { + this.epoch = epoch; + this.nodeInfoCollection = nodeInfoCollection; + } + + public Long getEpoch() { + return epoch; + } + + public List getNodeInfoCollection() { + return nodeInfoCollection; + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/netmap/NodeInfo.java b/modelsV2/src/main/java/info/FrostFS/sdk/jdo/netmap/NodeInfo.java index fe65459..5b060f1 100644 --- a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/netmap/NodeInfo.java +++ b/modelsV2/src/main/java/info/FrostFS/sdk/jdo/netmap/NodeInfo.java @@ -1,11 +1,26 @@ package info.FrostFS.sdk.jdo.netmap; -import info.FrostFS.sdk.jdo.Version; import info.FrostFS.sdk.enums.NodeState; +import info.FrostFS.sdk.jdo.Version; + +import java.util.List; +import java.util.Map; public class NodeInfo { public NodeState state; public Version version; + public List addresses; + public Map attributes; + public byte[] publicKey; + + public NodeInfo(NodeState state, Version version, List addresses, + Map attributes, byte[] publicKey) { + this.state = state; + this.version = version; + this.addresses = addresses; + this.attributes = attributes; + this.publicKey = publicKey; + } public NodeState getState() { return state; @@ -23,8 +38,15 @@ public class NodeInfo { this.version = version; } - public NodeInfo(NodeState state, Version version) { - this.state = state; - this.version = version; + public byte[] getPublicKey() { + return publicKey; + } + + public Map getAttributes() { + return attributes; + } + + public List getAddresses() { + return addresses; } } diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/SessionMapper.java b/modelsV2/src/main/java/info/FrostFS/sdk/mappers/SessionMapper.java new file mode 100644 index 0000000..f9d7ecd --- /dev/null +++ b/modelsV2/src/main/java/info/FrostFS/sdk/mappers/SessionMapper.java @@ -0,0 +1,31 @@ +package info.FrostFS.sdk.mappers; + +import com.google.protobuf.CodedOutputStream; +import com.google.protobuf.InvalidProtocolBufferException; +import frostFS.session.Types; + +import java.io.IOException; + +public class SessionMapper { + + public static byte[] serialize(Types.SessionToken token) + { + try { + byte[] bytes = new byte[token.getSerializedSize()]; + CodedOutputStream stream = CodedOutputStream.newInstance(bytes); + token.writeTo(stream); + return bytes; + } catch (IOException e) { + throw new IllegalArgumentException(e); + } + } + + public static Types.SessionToken deserializeSessionToken(byte[] bytes) + { + try { + return Types.SessionToken.newBuilder().mergeFrom(bytes).build(); + } catch (InvalidProtocolBufferException exp) { + throw new IllegalArgumentException(); + } + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/netmap/NetmapSnapshotMapper.java b/modelsV2/src/main/java/info/FrostFS/sdk/mappers/netmap/NetmapSnapshotMapper.java new file mode 100644 index 0000000..9d9eb27 --- /dev/null +++ b/modelsV2/src/main/java/info/FrostFS/sdk/mappers/netmap/NetmapSnapshotMapper.java @@ -0,0 +1,18 @@ +package info.FrostFS.sdk.mappers.netmap; + +import frostFS.netmap.Service; +import info.FrostFS.sdk.jdo.netmap.NetmapSnapshot; + +import java.util.stream.Collectors; + +public class NetmapSnapshotMapper { + + public static NetmapSnapshot toModel(Service.NetmapSnapshotResponse netmap) { + return new NetmapSnapshot( + netmap.getBody().getNetmap().getEpoch(), + netmap.getBody().getNetmap().getNodesList().stream() + .map(t -> NodeInfoMapper.toModel(t, netmap.getMetaHeader().getVersion())) + .collect(Collectors.toList()) + ); + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/netmap/NodeInfoMapper.java b/modelsV2/src/main/java/info/FrostFS/sdk/mappers/netmap/NodeInfoMapper.java index 934d60f..6c9e25c 100644 --- a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/netmap/NodeInfoMapper.java +++ b/modelsV2/src/main/java/info/FrostFS/sdk/mappers/netmap/NodeInfoMapper.java @@ -1,21 +1,36 @@ package info.FrostFS.sdk.mappers.netmap; import frostFS.netmap.Service; +import frostFS.netmap.Types.NodeInfo.Attribute; +import frostFS.refs.Types; import info.FrostFS.sdk.enums.NodeState; import info.FrostFS.sdk.jdo.netmap.NodeInfo; import info.FrostFS.sdk.mappers.VersionMapper; +import java.util.stream.Collectors; + import static java.util.Objects.isNull; public class NodeInfoMapper { public static NodeInfo toModel(Service.LocalNodeInfoResponse.Body nodeInfo) { - var nodeState = NodeState.get(nodeInfo.getNodeInfo().getState().getNumber()); + return toModel(nodeInfo.getNodeInfo(), nodeInfo.getVersion()); + } + + public static NodeInfo toModel(frostFS.netmap.Types.NodeInfo nodeInfo, Types.Version version) { + NodeState nodeState = NodeState.get(nodeInfo.getState().getNumber()); + if (isNull(nodeState)) { throw new IllegalArgumentException( - String.format("Unknown NodeState. Value: %s.", nodeInfo.getNodeInfo().getState()) + String.format("Unknown NodeState. Value: %s.", nodeInfo.getState()) ); } - return new NodeInfo(nodeState, VersionMapper.toModel(nodeInfo.getVersion())); + return new NodeInfo( + nodeState, + VersionMapper.toModel(version), + nodeInfo.getAddressesList(), + nodeInfo.getAttributesList().stream().collect(Collectors.toMap(Attribute::getKey, Attribute::getValue)), + nodeInfo.getPublicKey().toByteArray() + ); } } From b0db7df192e975da5da5629a524ca295053ddbc5 Mon Sep 17 00:00:00 2001 From: Bruk Ori Date: Mon, 22 Jul 2024 19:13:52 +0300 Subject: [PATCH 05/38] [#1] add client environment add client cut code cleanup Signed-off-by: Ori Bruk --- README.md | 32 +-- client/pom.xml | 12 +- .../src/main/java/info/FrostFS/sdk/Main.java | 51 ++-- .../info/FrostFS/sdk/RequestConstructor.java | 7 +- .../java/info/FrostFS/sdk/RequestSigner.java | 43 ++-- .../main/java/info/FrostFS/sdk/Verifier.java | 4 +- .../FrostFS/sdk/services/ContainerClient.java | 12 +- .../FrostFS/sdk/services/ContextAccessor.java | 6 +- .../FrostFS/sdk/services/FrostFSClient.java | 99 ++++---- .../FrostFS/sdk/services/NetmapClient.java | 11 +- .../FrostFS/sdk/services/ObjectClient.java | 21 +- .../FrostFS/sdk/services/SessionClient.java | 5 +- .../FrostFS/sdk/services/ToolsClient.java | 6 +- .../sdk/services/impl/ContainerService.java | 46 ++-- .../sdk/services/impl/NetmapService.java | 28 +-- .../sdk/services/impl/ObjectService.java | 228 +++++++++++++----- .../sdk/services/impl/ObjectTools.java | 106 ++++++++ .../sdk/services/impl/SessionService.java | 18 +- .../FrostFS/sdk/tools/ClientEnvironment.java | 25 +- .../sdk/{ => tools}/ClientSettings.java | 2 +- .../java/info/FrostFS/sdk/tools/ECDsa.java | 29 +++ .../sdk/tools/PutObjectParameters.java | 76 ++++++ cryptography/pom.xml | 10 - .../main/java/info/FrostFS/sdk/Helper.java | 10 +- .../java/info/FrostFS/sdk/KeyExtension.java | 18 +- {modelsV2 => models}/pom.xml | 4 +- .../java/info/FrostFS/sdk/UUIDExtension.java | 0 .../info/FrostFS/sdk/constants/AppConst.java | 4 +- .../FrostFS/sdk/constants/XHeaderConst.java | 0 .../info/FrostFS/sdk/dto}/MetaHeader.java | 8 +- .../java/info/FrostFS/sdk/dto}/OwnerId.java | 2 +- .../info/FrostFS/sdk/dto}/SessionToken.java | 6 +- .../java/info/FrostFS/sdk/dto/Signature.java | 33 +++ .../main/java/info/FrostFS/sdk/dto/Split.java | 69 ++++++ .../java/info/FrostFS/sdk/dto/SplitId.java | 44 ++++ .../java/info/FrostFS/sdk/dto}/Status.java | 12 +- .../java/info/FrostFS/sdk/dto}/Version.java | 16 +- .../FrostFS/sdk/dto/container}/Container.java | 32 +-- .../sdk/dto/container}/ContainerId.java | 7 +- .../sdk/dto}/netmap/NetmapSnapshot.java | 6 +- .../FrostFS/sdk/dto}/netmap/NodeInfo.java | 22 +- .../sdk/dto}/netmap/PlacementPolicy.java | 14 +- .../info/FrostFS/sdk/dto}/netmap/Replica.java | 10 +- .../FrostFS/sdk/dto/object/LargeObject.java | 29 +++ .../FrostFS/sdk/dto/object/LinkObject.java | 21 ++ .../sdk/dto/object}/ObjectAttribute.java | 6 +- .../FrostFS/sdk/dto/object}/ObjectFilter.java | 14 +- .../FrostFS/sdk/dto/object/ObjectFrostFS.java | 80 ++++++ .../FrostFS/sdk/dto/object/ObjectHeader.java | 118 +++++++++ .../FrostFS/sdk/dto/object}/ObjectId.java | 7 +- .../java/info/FrostFS/sdk/enums/BasicAcl.java | 2 +- .../info/FrostFS/sdk/enums/NodeState.java | 0 .../FrostFS/sdk/enums/ObjectMatchType.java | 0 .../info/FrostFS/sdk/enums/ObjectType.java | 0 .../FrostFS/sdk/enums/SignatureScheme.java | 14 ++ .../info/FrostFS/sdk/enums/StatusCode.java | 0 .../FrostFS/sdk/mappers/MetaHeaderMapper.java | 2 +- .../FrostFS/sdk/mappers/OwnerIdMapper.java | 3 +- .../FrostFS/sdk/mappers/SessionMapper.java | 12 +- .../FrostFS/sdk/mappers/SignatureMapper.java | 25 ++ .../FrostFS/sdk/mappers/StatusMapper.java | 4 +- .../FrostFS/sdk/mappers/VersionMapper.java | 2 +- .../mappers/container}/ContainerIdMapper.java | 6 +- .../mappers/container}/ContainerMapper.java | 22 +- .../mappers/netmap/NetmapSnapshotMapper.java | 4 +- .../sdk/mappers/netmap/NodeInfoMapper.java | 4 +- .../mappers/netmap/PlacementPolicyMapper.java | 5 +- .../sdk/mappers/netmap/ReplicaMapper.java | 3 +- .../object}/ObjectAttributeMapper.java | 5 +- .../mappers/object}/ObjectFilterMapper.java | 6 +- .../mappers/object/ObjectFrostFSMapper.java | 16 ++ .../mappers/object}/ObjectHeaderMapper.java | 18 +- .../sdk/mappers/object}/ObjectIdMapper.java | 9 +- .../info/FrostFS/sdk/jdo/ObjectFrostFs.java | 37 --- .../info/FrostFS/sdk/jdo/ObjectHeader.java | 71 ------ .../FrostFS/sdk/mappers/ObjectMapper.java | 15 -- pom.xml | 4 +- {protosV2 => protos}/pom.xml | 4 +- .../src/main/proto/accounting/service.proto | 0 .../src/main/proto/accounting/types.proto | 0 .../src/main/proto/acl/types.proto | 0 .../src/main/proto/apemanager/service.proto | 0 .../src/main/proto/apemanager/types.proto | 0 .../src/main/proto/container/service.proto | 0 .../src/main/proto/container/types.proto | 0 .../src/main/proto/lock/types.proto | 0 .../src/main/proto/netmap/service.proto | 0 .../src/main/proto/netmap/types.proto | 0 .../src/main/proto/object/service.proto | 0 .../src/main/proto/object/types.proto | 0 .../src/main/proto/refs/types.proto | 0 .../src/main/proto/session/service.proto | 0 .../src/main/proto/session/types.proto | 0 .../src/main/proto/status/types.proto | 0 .../src/main/proto/tombstone/types.proto | 0 95 files changed, 1202 insertions(+), 560 deletions(-) create mode 100644 client/src/main/java/info/FrostFS/sdk/services/impl/ObjectTools.java rename client/src/main/java/info/FrostFS/sdk/{ => tools}/ClientSettings.java (97%) create mode 100644 client/src/main/java/info/FrostFS/sdk/tools/ECDsa.java create mode 100644 client/src/main/java/info/FrostFS/sdk/tools/PutObjectParameters.java rename {modelsV2 => models}/pom.xml (93%) rename {modelsV2 => models}/src/main/java/info/FrostFS/sdk/UUIDExtension.java (100%) rename modelsV2/src/main/java/info/FrostFS/sdk/jdo/Constants.java => models/src/main/java/info/FrostFS/sdk/constants/AppConst.java (66%) rename {modelsV2 => models}/src/main/java/info/FrostFS/sdk/constants/XHeaderConst.java (100%) rename {modelsV2/src/main/java/info/FrostFS/sdk/jdo => models/src/main/java/info/FrostFS/sdk/dto}/MetaHeader.java (86%) rename {modelsV2/src/main/java/info/FrostFS/sdk/jdo => models/src/main/java/info/FrostFS/sdk/dto}/OwnerId.java (94%) rename {modelsV2/src/main/java/info/FrostFS/sdk/jdo => models/src/main/java/info/FrostFS/sdk/dto}/SessionToken.java (74%) create mode 100644 models/src/main/java/info/FrostFS/sdk/dto/Signature.java create mode 100644 models/src/main/java/info/FrostFS/sdk/dto/Split.java create mode 100644 models/src/main/java/info/FrostFS/sdk/dto/SplitId.java rename {modelsV2/src/main/java/info/FrostFS/sdk/jdo => models/src/main/java/info/FrostFS/sdk/dto}/Status.java (71%) rename {modelsV2/src/main/java/info/FrostFS/sdk/jdo => models/src/main/java/info/FrostFS/sdk/dto}/Version.java (88%) rename {modelsV2/src/main/java/info/FrostFS/sdk/jdo => models/src/main/java/info/FrostFS/sdk/dto/container}/Container.java (64%) rename {modelsV2/src/main/java/info/FrostFS/sdk/jdo => models/src/main/java/info/FrostFS/sdk/dto/container}/ContainerId.java (78%) rename {modelsV2/src/main/java/info/FrostFS/sdk/jdo => models/src/main/java/info/FrostFS/sdk/dto}/netmap/NetmapSnapshot.java (75%) rename {modelsV2/src/main/java/info/FrostFS/sdk/jdo => models/src/main/java/info/FrostFS/sdk/dto}/netmap/NodeInfo.java (66%) rename {modelsV2/src/main/java/info/FrostFS/sdk/jdo => models/src/main/java/info/FrostFS/sdk/dto}/netmap/PlacementPolicy.java (52%) rename {modelsV2/src/main/java/info/FrostFS/sdk/jdo => models/src/main/java/info/FrostFS/sdk/dto}/netmap/Replica.java (72%) create mode 100644 models/src/main/java/info/FrostFS/sdk/dto/object/LargeObject.java create mode 100644 models/src/main/java/info/FrostFS/sdk/dto/object/LinkObject.java rename {modelsV2/src/main/java/info/FrostFS/sdk/jdo => models/src/main/java/info/FrostFS/sdk/dto/object}/ObjectAttribute.java (82%) rename {modelsV2/src/main/java/info/FrostFS/sdk/jdo => models/src/main/java/info/FrostFS/sdk/dto/object}/ObjectFilter.java (82%) create mode 100644 models/src/main/java/info/FrostFS/sdk/dto/object/ObjectFrostFS.java create mode 100644 models/src/main/java/info/FrostFS/sdk/dto/object/ObjectHeader.java rename {modelsV2/src/main/java/info/FrostFS/sdk/jdo => models/src/main/java/info/FrostFS/sdk/dto/object}/ObjectId.java (75%) rename {modelsV2 => models}/src/main/java/info/FrostFS/sdk/enums/BasicAcl.java (93%) rename {modelsV2 => models}/src/main/java/info/FrostFS/sdk/enums/NodeState.java (100%) rename {modelsV2 => models}/src/main/java/info/FrostFS/sdk/enums/ObjectMatchType.java (100%) rename {modelsV2 => models}/src/main/java/info/FrostFS/sdk/enums/ObjectType.java (100%) create mode 100644 models/src/main/java/info/FrostFS/sdk/enums/SignatureScheme.java rename {modelsV2 => models}/src/main/java/info/FrostFS/sdk/enums/StatusCode.java (100%) rename {modelsV2 => models}/src/main/java/info/FrostFS/sdk/mappers/MetaHeaderMapper.java (91%) rename {modelsV2 => models}/src/main/java/info/FrostFS/sdk/mappers/OwnerIdMapper.java (89%) rename {modelsV2 => models}/src/main/java/info/FrostFS/sdk/mappers/SessionMapper.java (73%) create mode 100644 models/src/main/java/info/FrostFS/sdk/mappers/SignatureMapper.java rename {modelsV2 => models}/src/main/java/info/FrostFS/sdk/mappers/StatusMapper.java (94%) rename {modelsV2 => models}/src/main/java/info/FrostFS/sdk/mappers/VersionMapper.java (92%) rename {modelsV2/src/main/java/info/FrostFS/sdk/mappers => models/src/main/java/info/FrostFS/sdk/mappers/container}/ContainerIdMapper.java (73%) rename {modelsV2/src/main/java/info/FrostFS/sdk/mappers => models/src/main/java/info/FrostFS/sdk/mappers/container}/ContainerMapper.java (60%) rename {modelsV2 => models}/src/main/java/info/FrostFS/sdk/mappers/netmap/NetmapSnapshotMapper.java (74%) rename {modelsV2 => models}/src/main/java/info/FrostFS/sdk/mappers/netmap/NodeInfoMapper.java (96%) rename {modelsV2 => models}/src/main/java/info/FrostFS/sdk/mappers/netmap/PlacementPolicyMapper.java (88%) rename {modelsV2 => models}/src/main/java/info/FrostFS/sdk/mappers/netmap/ReplicaMapper.java (91%) rename {modelsV2/src/main/java/info/FrostFS/sdk/mappers => models/src/main/java/info/FrostFS/sdk/mappers/object}/ObjectAttributeMapper.java (83%) rename {modelsV2/src/main/java/info/FrostFS/sdk/mappers => models/src/main/java/info/FrostFS/sdk/mappers/object}/ObjectFilterMapper.java (86%) create mode 100644 models/src/main/java/info/FrostFS/sdk/mappers/object/ObjectFrostFSMapper.java rename {modelsV2/src/main/java/info/FrostFS/sdk/mappers => models/src/main/java/info/FrostFS/sdk/mappers/object}/ObjectHeaderMapper.java (75%) rename {modelsV2/src/main/java/info/FrostFS/sdk/mappers => models/src/main/java/info/FrostFS/sdk/mappers/object}/ObjectIdMapper.java (57%) delete mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/jdo/ObjectFrostFs.java delete mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/jdo/ObjectHeader.java delete mode 100644 modelsV2/src/main/java/info/FrostFS/sdk/mappers/ObjectMapper.java rename {protosV2 => protos}/pom.xml (98%) rename {protosV2 => protos}/src/main/proto/accounting/service.proto (100%) rename {protosV2 => protos}/src/main/proto/accounting/types.proto (100%) rename {protosV2 => protos}/src/main/proto/acl/types.proto (100%) rename {protosV2 => protos}/src/main/proto/apemanager/service.proto (100%) rename {protosV2 => protos}/src/main/proto/apemanager/types.proto (100%) rename {protosV2 => protos}/src/main/proto/container/service.proto (100%) rename {protosV2 => protos}/src/main/proto/container/types.proto (100%) rename {protosV2 => protos}/src/main/proto/lock/types.proto (100%) rename {protosV2 => protos}/src/main/proto/netmap/service.proto (100%) rename {protosV2 => protos}/src/main/proto/netmap/types.proto (100%) rename {protosV2 => protos}/src/main/proto/object/service.proto (100%) rename {protosV2 => protos}/src/main/proto/object/types.proto (100%) rename {protosV2 => protos}/src/main/proto/refs/types.proto (100%) rename {protosV2 => protos}/src/main/proto/session/service.proto (100%) rename {protosV2 => protos}/src/main/proto/session/types.proto (100%) rename {protosV2 => protos}/src/main/proto/status/types.proto (100%) rename {protosV2 => protos}/src/main/proto/tombstone/types.proto (100%) diff --git a/README.md b/README.md index 2883006..94031a8 100644 --- a/README.md +++ b/README.md @@ -22,9 +22,9 @@ neo-go wallet export -w -d ```java import info.FrostFS.sdk.enums.BasicAcl; -import info.FrostFS.sdk.jdo.Container; -import info.FrostFS.sdk.jdo.netmap.PlacementPolicy; -import info.FrostFS.sdk.jdo.netmap.Replica; +import info.FrostFS.sdk.dto.container.Container; +import info.FrostFS.sdk.dto.netmap.PlacementPolicy; +import info.FrostFS.sdk.dto.netmap.Replica; import info.FrostFS.sdk.services.FrostFSClient; public class ContainerExample { @@ -36,16 +36,16 @@ public class ContainerExample { // Create container var placementPolicy = new PlacementPolicy(true, new Replica[]{new Replica(1)}); - var containerId = frostFSClient.createContainerAsync(new Container(BasicAcl.PUBLIC_RW, placementPolicy)); + var containerId = frostFSClient.createContainer(new Container(BasicAcl.PUBLIC_RW, placementPolicy)); // Get container - var container = frostFSClient.getContainerAsync(containerId); + var container = frostFSClient.getContainer(containerId); // List containers - var containerIds = frostFSClient.listContainersAsync(); + var containerIds = frostFSClient.listContainers(); // Delete container - frostFSClient.deleteContainerAsync(containerId); + frostFSClient.deleteContainer(containerId); } } ``` @@ -54,10 +54,10 @@ public class ContainerExample { ```java import info.FrostFS.sdk.enums.ObjectType; -import info.FrostFS.sdk.jdo.ContainerId; -import info.FrostFS.sdk.jdo.ObjectAttribute; -import info.FrostFS.sdk.jdo.ObjectFilter; -import info.FrostFS.sdk.jdo.ObjectHeader; +import info.FrostFS.sdk.dto.container.ContainerId; +import info.FrostFS.sdk.dto.object.ObjectAttribute; +import info.FrostFS.sdk.dto.object.ObjectFilter; +import info.FrostFS.sdk.dto.object.ObjectHeader; import info.FrostFS.sdk.services.FrostFSClient; import java.io.FileInputStream; @@ -71,24 +71,24 @@ public class ObjectExample { FrostFSClient frostFSClient = new FrostFSClient(grpcClient, client); // Put object - info.FrostFS.sdk.jdo.ObjectId objectId; + info.FrostFS.sdk.dto.object.ObjectId objectId; try (FileInputStream fis = new FileInputStream("cat.jpg")) { var cat = new ObjectHeader( containerId, ObjectType.REGULAR, new ObjectAttribute[]{new ObjectAttribute("Filename", "cat.jpg")} ); - objectId = frostFSClient.putObjectAsync(cat, fis); + objectId = frostFSClient.putObject(cat, fis); } catch (IOException e) { throw new RuntimeException(e); } // Get object - var obj = frostFSClient.getObjectAsync(containerId, objectId); + var obj = frostFSClient.getObject(containerId, objectId); // Get object header - var objectHeader = frostFSClient.getObjectHeadAsync(containerId, objectId); + var objectHeader = frostFSClient.getObjectHead(containerId, objectId); // Search regular objects - var objectIds = frostFSClient.searchObjectsAsync(containerId, ObjectFilter.RootFilter()); + var objectIds = frostFSClient.searchObjects(containerId, ObjectFilter.RootFilter()); } } ``` \ No newline at end of file diff --git a/client/pom.xml b/client/pom.xml index b416ee4..3d3426e 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -25,9 +25,19 @@ info.FrostFS.sdk - modelsV2 + models 0.1.0 + + org.apache.commons + commons-collections4 + 4.4 + + + commons-codec + commons-codec + 1.17.0 + \ No newline at end of file diff --git a/client/src/main/java/info/FrostFS/sdk/Main.java b/client/src/main/java/info/FrostFS/sdk/Main.java index d16f06e..4c854fe 100644 --- a/client/src/main/java/info/FrostFS/sdk/Main.java +++ b/client/src/main/java/info/FrostFS/sdk/Main.java @@ -1,14 +1,22 @@ package info.FrostFS.sdk; +import info.FrostFS.sdk.dto.container.Container; +import info.FrostFS.sdk.dto.container.ContainerId; import info.FrostFS.sdk.enums.BasicAcl; import info.FrostFS.sdk.enums.ObjectType; -import info.FrostFS.sdk.jdo.*; -import info.FrostFS.sdk.jdo.netmap.PlacementPolicy; -import info.FrostFS.sdk.jdo.netmap.Replica; +import info.FrostFS.sdk.dto.netmap.PlacementPolicy; +import info.FrostFS.sdk.dto.netmap.Replica; +import info.FrostFS.sdk.dto.object.ObjectAttribute; +import info.FrostFS.sdk.dto.object.ObjectFrostFS; +import info.FrostFS.sdk.dto.object.ObjectHeader; +import info.FrostFS.sdk.dto.object.ObjectId; import info.FrostFS.sdk.services.FrostFSClient; +import info.FrostFS.sdk.tools.ClientSettings; +import info.FrostFS.sdk.tools.PutObjectParameters; import java.io.FileInputStream; import java.io.FileNotFoundException; +import java.util.Collections; import java.util.List; public class Main { @@ -18,37 +26,42 @@ public class Main { /* var res2 = frostFSClient.searchObjectsAsync(new ContainerId("EQGx2QeYHJb53uRwYGzcQaW191sZpdNrjutk6veUSV2R")); */ - - ObjectFrostFs res3 = frostFSClient - .getObjectAsync(new ContainerId("EQGx2QeYHJb53uRwYGzcQaW191sZpdNrjutk6veUSV2R"), new ObjectId("85orCLKSu3X1jGiTFmwmTUsBU88RBARNwuRwrEy5pyww")); - - - var ttttt = frostFSClient.getNetworkSettingsAsync(); - Container container2 = frostFSClient.getContainerAsync(new ContainerId("EQGx2QeYHJb53uRwYGzcQaW191sZpdNrjutk6veUSV2R")); - - var placementPolicy = new PlacementPolicy(true, new Replica[]{new Replica(1)}); - var containerId = frostFSClient.createContainerAsync(new Container(BasicAcl.PUBLIC_RW, placementPolicy)); + var containerId = frostFSClient.createContainer(new Container(BasicAcl.PUBLIC_RW, placementPolicy)); Thread.sleep(1000); FileInputStream file = null; try { file = new FileInputStream("/home/ori/Desktop/cat.jpg"); - var cat = new ObjectHeader(containerId, ObjectType.REGULAR, new ObjectAttribute[]{new ObjectAttribute("Filename", "cat.jpg")}); - frostFSClient.putObjectAsync(cat, file); + var cat = new ObjectHeader(containerId, ObjectType.REGULAR, Collections.singletonList(new ObjectAttribute("Filename", "cat3.jpg"))); + + var params = new PutObjectParameters(cat, file, false, 1024); + var tty = frostFSClient.putObject(params); + + System.out.println(1); } catch (FileNotFoundException e) { throw new RuntimeException(e); } - var res = frostFSClient.searchObjectsAsync(containerId); + + var res = frostFSClient.searchObjects(containerId); + + ObjectFrostFS res3 = frostFSClient + .getObject(new ContainerId("EQGx2QeYHJb53uRwYGzcQaW191sZpdNrjutk6veUSV2R"), new ObjectId("85orCLKSu3X1jGiTFmwmTUsBU88RBARNwuRwrEy5pyww")); - Container container = frostFSClient.getContainerAsync(containerId); - List containerIds = frostFSClient.listContainersAsync(); + var ttttt = frostFSClient.getNetworkSettings(); + Container container2 = frostFSClient.getContainer(new ContainerId("EQGx2QeYHJb53uRwYGzcQaW191sZpdNrjutk6veUSV2R")); + + + + + Container container = frostFSClient.getContainer(containerId); + List containerIds = frostFSClient.listContainers(); /* frostFSClient.deleteContainerAsync(containerId); */ - containerIds = frostFSClient.listContainersAsync(); + containerIds = frostFSClient.listContainers(); System.out.println(); } } \ No newline at end of file diff --git a/client/src/main/java/info/FrostFS/sdk/RequestConstructor.java b/client/src/main/java/info/FrostFS/sdk/RequestConstructor.java index 304a878..28daecc 100644 --- a/client/src/main/java/info/FrostFS/sdk/RequestConstructor.java +++ b/client/src/main/java/info/FrostFS/sdk/RequestConstructor.java @@ -2,8 +2,9 @@ package info.FrostFS.sdk; import com.google.protobuf.AbstractMessage; import frostFS.session.Types; -import info.FrostFS.sdk.jdo.MetaHeader; +import info.FrostFS.sdk.dto.MetaHeader; import info.FrostFS.sdk.mappers.MetaHeaderMapper; +import info.FrostFS.sdk.tools.ECDsa; import static info.FrostFS.sdk.RequestSigner.signMessagePart; import static java.util.Objects.isNull; @@ -27,7 +28,7 @@ public class RequestConstructor { frostFS.refs.Types.ContainerID cid, frostFS.refs.Types.ObjectID oid, Types.ObjectSessionContext.Verb verb, - byte[] publicKey, byte[] privateKey) { + ECDsa key) { var headerField = request.getDescriptorForType().findFieldByName("meta_header"); var header = (Types.RequestMetaHeader) request.getField(headerField); if (header.getSessionToken().getSerializedSize() > 0) { @@ -41,7 +42,7 @@ public class RequestConstructor { var body = sessionToken.getBody().toBuilder().setObject(ctx).build(); sessionToken = sessionToken.toBuilder() - .setSignature(signMessagePart(publicKey, privateKey, body)) + .setSignature(signMessagePart(key, body)) .setBody(body) .build(); diff --git a/client/src/main/java/info/FrostFS/sdk/RequestSigner.java b/client/src/main/java/info/FrostFS/sdk/RequestSigner.java index 962a421..a26dadf 100644 --- a/client/src/main/java/info/FrostFS/sdk/RequestSigner.java +++ b/client/src/main/java/info/FrostFS/sdk/RequestSigner.java @@ -3,6 +3,7 @@ package info.FrostFS.sdk; import com.google.protobuf.AbstractMessage; import com.google.protobuf.ByteString; import frostFS.session.Types; +import info.FrostFS.sdk.tools.ECDsa; import org.apache.commons.codec.digest.DigestUtils; import org.bouncycastle.asn1.sec.SECNamedCurves; import org.bouncycastle.asn1.sec.SECObjectIdentifiers; @@ -14,19 +15,18 @@ import org.bouncycastle.crypto.signers.HMacDSAKCalculator; import java.math.BigInteger; import java.security.Signature; -import static info.FrostFS.sdk.KeyExtension.loadPrivateKey; import static org.bouncycastle.crypto.util.DigestFactory.createSHA256; import static org.bouncycastle.util.BigIntegers.asUnsignedByteArray; public class RequestSigner { public static final int RFC6979_SIGNATURE_SIZE = 64; - public static byte[] signData(byte[] privateKey, byte[] data) { + public static byte[] signData(ECDsa key, byte[] data) { var hash = new byte[65]; hash[0] = 0x04; try { Signature signature = Signature.getInstance("NONEwithECDSAinP1363Format"); - signature.initSign(loadPrivateKey(privateKey)); + signature.initSign(key.getPrivateKey()); signature.update(DigestUtils.sha512(data)); byte[] sig = signature.sign(); System.arraycopy(sig, 0, hash, 1, sig.length); @@ -37,12 +37,12 @@ public class RequestSigner { return hash; } - public static byte[] signRFC6979(byte[] privateKey, byte[] data) { + public static byte[] signRFC6979(ECDsa key, byte[] data) { var digest = createSHA256(); var secp256R1 = SECNamedCurves.getByOID(SECObjectIdentifiers.secp256r1); var ecParameters = new ECDomainParameters(secp256R1.getCurve(), secp256R1.getG(), secp256R1.getN()); - var ecPrivateKey = new ECPrivateKeyParameters(new BigInteger(1, privateKey), ecParameters); + var ecPrivateKey = new ECPrivateKeyParameters(new BigInteger(1, key.getPrivateKeyByte()), ecParameters); var signer = new ECDSASigner(new HMacDSAKCalculator(digest)); var hash = new byte[digest.getDigestSize()]; @@ -62,36 +62,29 @@ public class RequestSigner { return signature; } - public static frostFS.refs.Types.SignatureRFC6979 signRFC6979(byte[] publicKey, - byte[] privateKey, - AbstractMessage message) { + public static frostFS.refs.Types.SignatureRFC6979 signRFC6979(ECDsa key, AbstractMessage message) { return frostFS.refs.Types.SignatureRFC6979.newBuilder() - .setKey(ByteString.copyFrom(publicKey)) - .setSign(ByteString.copyFrom(signRFC6979(privateKey, message.toByteArray()))) + .setKey(ByteString.copyFrom(key.getPublicKeyByte())) + .setSign(ByteString.copyFrom(signRFC6979(key, message.toByteArray()))) .build(); } - public static frostFS.refs.Types.SignatureRFC6979 signRFC6979(byte[] publicKey, - byte[] privateKey, - ByteString data) { + public static frostFS.refs.Types.SignatureRFC6979 signRFC6979(ECDsa key, ByteString data) { return frostFS.refs.Types.SignatureRFC6979.newBuilder() - .setKey(ByteString.copyFrom(publicKey)) - .setSign(ByteString.copyFrom(signRFC6979(privateKey, data.toByteArray()))) + .setKey(ByteString.copyFrom(key.getPublicKeyByte())) + .setSign(ByteString.copyFrom(signRFC6979(key, data.toByteArray()))) .build(); } - public static frostFS.refs.Types.Signature signMessagePart(byte[] publicKey, - byte[] privateKey, - AbstractMessage data) { + public static frostFS.refs.Types.Signature signMessagePart(ECDsa key, AbstractMessage data) { var data2Sign = data.getSerializedSize() == 0 ? new byte[]{} : data.toByteArray(); - return frostFS.refs.Types.Signature.newBuilder() - .setKey(ByteString.copyFrom(publicKey)) - .setSign(ByteString.copyFrom(signData(privateKey, data2Sign))) + .setKey(ByteString.copyFrom(key.getPublicKeyByte())) + .setSign(ByteString.copyFrom(signData(key, data2Sign))) .build(); } - public static void sign(AbstractMessage.Builder request, byte[] publicKey, byte[] privateKey) { + public static void sign(AbstractMessage.Builder request, ECDsa key) { var meta = (AbstractMessage) request.getField(request.getDescriptorForType().findFieldByName("meta_header")); var body = (AbstractMessage) request.getField(request.getDescriptorForType().findFieldByName("body")); var verify = (AbstractMessage) request.getField(request.getDescriptorForType().findFieldByName("verify_header")); @@ -109,7 +102,7 @@ public class RequestSigner { if (verifyOrigin.getSerializedSize() == 0) { verifyBuilder.setField( verifyBuilder.getDescriptorForType().findFieldByName("body_signature"), - signMessagePart(publicKey, privateKey, body) + signMessagePart(key, body) ); } else { verifyBuilder.setField(verifyBuilder.getDescriptorForType().findFieldByName("origin"), verifyOrigin); @@ -117,11 +110,11 @@ public class RequestSigner { verifyBuilder.setField( verifyBuilder.getDescriptorForType().findFieldByName("meta_signature"), - signMessagePart(publicKey, privateKey, meta) + signMessagePart(key, meta) ); verifyBuilder.setField( verifyBuilder.getDescriptorForType().findFieldByName("origin_signature"), - signMessagePart(publicKey, privateKey, verifyOrigin) + signMessagePart(key, verifyOrigin) ); request.setField(request.getDescriptorForType().findFieldByName("verify_header"), verifyBuilder.build()); diff --git a/client/src/main/java/info/FrostFS/sdk/Verifier.java b/client/src/main/java/info/FrostFS/sdk/Verifier.java index 7a1b136..e0c37ec 100644 --- a/client/src/main/java/info/FrostFS/sdk/Verifier.java +++ b/client/src/main/java/info/FrostFS/sdk/Verifier.java @@ -16,7 +16,7 @@ import java.security.PublicKey; import java.security.Signature; import java.util.Arrays; -import static info.FrostFS.sdk.KeyExtension.getPublicKeyByPublic; +import static info.FrostFS.sdk.KeyExtension.getPublicKeyFromBytes; import static java.util.Objects.isNull; import static org.bouncycastle.crypto.util.DigestFactory.createSHA256; import static org.bouncycastle.util.BigIntegers.fromUnsignedByteArray; @@ -99,7 +99,7 @@ public class Verifier { public static boolean verifyMessagePart(Types.Signature sig, AbstractMessage data) { if (sig.getSerializedSize() == 0 || sig.getKey().isEmpty() || sig.getSign().isEmpty()) return false; - var publicKey = getPublicKeyByPublic(sig.getKey().toByteArray()); + var publicKey = getPublicKeyFromBytes(sig.getKey().toByteArray()); var data2Verify = data.getSerializedSize() == 0 ? new byte[]{} : data.toByteArray(); return verifyData(publicKey, data2Verify, sig.getSign().toByteArray()); diff --git a/client/src/main/java/info/FrostFS/sdk/services/ContainerClient.java b/client/src/main/java/info/FrostFS/sdk/services/ContainerClient.java index 3cdb8c2..385f616 100644 --- a/client/src/main/java/info/FrostFS/sdk/services/ContainerClient.java +++ b/client/src/main/java/info/FrostFS/sdk/services/ContainerClient.java @@ -1,16 +1,16 @@ package info.FrostFS.sdk.services; -import info.FrostFS.sdk.jdo.Container; -import info.FrostFS.sdk.jdo.ContainerId; +import info.FrostFS.sdk.dto.container.Container; +import info.FrostFS.sdk.dto.container.ContainerId; import java.util.List; public interface ContainerClient { - Container getContainerAsync(ContainerId cid); + Container getContainer(ContainerId cid); - List listContainersAsync(); + List listContainers(); - ContainerId createContainerAsync(Container container); + ContainerId createContainer(Container container); - void deleteContainerAsync(ContainerId cid); + void deleteContainer(ContainerId cid); } diff --git a/client/src/main/java/info/FrostFS/sdk/services/ContextAccessor.java b/client/src/main/java/info/FrostFS/sdk/services/ContextAccessor.java index 6985310..d3880a9 100644 --- a/client/src/main/java/info/FrostFS/sdk/services/ContextAccessor.java +++ b/client/src/main/java/info/FrostFS/sdk/services/ContextAccessor.java @@ -3,7 +3,7 @@ package info.FrostFS.sdk.services; import info.FrostFS.sdk.tools.ClientEnvironment; public class ContextAccessor { - protected ClientEnvironment context; + private final ClientEnvironment context; public ContextAccessor(ClientEnvironment context) { this.context = context; @@ -12,8 +12,4 @@ public class ContextAccessor { public ClientEnvironment getContext() { return context; } - - public void setContext(ClientEnvironment context) { - this.context = context; - } } diff --git a/client/src/main/java/info/FrostFS/sdk/services/FrostFSClient.java b/client/src/main/java/info/FrostFS/sdk/services/FrostFSClient.java index 44a0388..64ba93c 100644 --- a/client/src/main/java/info/FrostFS/sdk/services/FrostFSClient.java +++ b/client/src/main/java/info/FrostFS/sdk/services/FrostFSClient.java @@ -1,19 +1,23 @@ package info.FrostFS.sdk.services; import frostFS.session.Types; -import info.FrostFS.sdk.ClientSettings; -import info.FrostFS.sdk.jdo.*; -import info.FrostFS.sdk.jdo.netmap.NetmapSnapshot; -import info.FrostFS.sdk.jdo.netmap.NodeInfo; -import info.FrostFS.sdk.services.impl.ContainerService; -import info.FrostFS.sdk.services.impl.NetmapService; -import info.FrostFS.sdk.services.impl.ObjectService; -import info.FrostFS.sdk.services.impl.SessionService; +import info.FrostFS.sdk.tools.ClientSettings; +import info.FrostFS.sdk.tools.PutObjectParameters; +import info.FrostFS.sdk.dto.container.Container; +import info.FrostFS.sdk.dto.container.ContainerId; +import info.FrostFS.sdk.dto.SessionToken; +import info.FrostFS.sdk.dto.Version; +import info.FrostFS.sdk.dto.netmap.NetmapSnapshot; +import info.FrostFS.sdk.dto.netmap.NodeInfo; +import info.FrostFS.sdk.dto.object.ObjectFilter; +import info.FrostFS.sdk.dto.object.ObjectFrostFS; +import info.FrostFS.sdk.dto.object.ObjectHeader; +import info.FrostFS.sdk.dto.object.ObjectId; +import info.FrostFS.sdk.services.impl.*; import info.FrostFS.sdk.tools.ClientEnvironment; import info.FrostFS.sdk.tools.NetworkSettings; import io.grpc.Channel; -import java.io.FileInputStream; import java.util.List; import static info.FrostFS.sdk.GrpcClient.initGrpcChannel; @@ -24,7 +28,7 @@ public class FrostFSClient implements ContainerClient, ObjectClient, NetmapClien private final NetmapService netmapService; private final ObjectService objectService; private final SessionService sessionService; - private final ClientEnvironment clientEnvironment; + private final ObjectTools objectTools; public FrostFSClient(ClientSettings clientSettings) { if (isNull(clientSettings)) { @@ -32,20 +36,22 @@ public class FrostFSClient implements ContainerClient, ObjectClient, NetmapClien } clientSettings.validate(); + Channel channel = initGrpcChannel(clientSettings.getHost(), clientSettings.getCreds()); - this.clientEnvironment = + ClientEnvironment clientEnvironment = new ClientEnvironment(clientSettings.getKey(), channel, new Version(2, 13), this); this.containerService = new ContainerService(clientEnvironment); this.netmapService = new NetmapService(clientEnvironment); this.sessionService = new SessionService(clientEnvironment); this.objectService = new ObjectService(clientEnvironment); + this.objectTools = new ObjectTools(clientEnvironment); checkFrostFsVersionSupport(clientEnvironment.getVersion()); } private void checkFrostFsVersionSupport(Version version) { - var localNodeInfo = netmapService.getLocalNodeInfoAsync(); + var localNodeInfo = netmapService.getLocalNodeInfo(); if (!localNodeInfo.getVersion().isSupported(version)) { var msg = String.format("FrostFS %s is not supported.", localNodeInfo.getVersion()); System.out.println(msg); @@ -54,81 +60,76 @@ public class FrostFSClient implements ContainerClient, ObjectClient, NetmapClien } @Override - public Container getContainerAsync(ContainerId cid) { - return containerService.getContainerAsync(cid); + public Container getContainer(ContainerId cid) { + return containerService.getContainer(cid); } @Override - public List listContainersAsync() { - return containerService.listContainersAsync(); + public List listContainers() { + return containerService.listContainers(); } @Override - public ContainerId createContainerAsync(Container container) { - return containerService.createContainerAsync(container); + public ContainerId createContainer(Container container) { + return containerService.createContainer(container); } @Override - public void deleteContainerAsync(ContainerId cid) { - containerService.deleteContainerAsync(cid); + public void deleteContainer(ContainerId cid) { + containerService.deleteContainer(cid); } @Override - public ObjectHeader getObjectHeadAsync(ContainerId containerId, ObjectId objectId) { - return objectService.getObjectHeadAsync(containerId, objectId); + public ObjectHeader getObjectHead(ContainerId containerId, ObjectId objectId) { + return objectService.getObjectHead(containerId, objectId); } @Override - public ObjectFrostFs getObjectAsync(ContainerId containerId, ObjectId objectId) { - return objectService.getObjectAsync(containerId, objectId); + public ObjectFrostFS getObject(ContainerId containerId, ObjectId objectId) { + return objectService.getObject(containerId, objectId); } @Override - public ObjectId putObjectAsync(ObjectHeader header, FileInputStream payload) { - return objectService.putObjectAsync(header, payload); + public ObjectId putObject(PutObjectParameters parameters) { + return objectService.putObject(parameters); } @Override - public ObjectId putObjectAsync(ObjectHeader header, byte[] payload) { - return objectService.putObjectAsync(header, payload); + public void deleteObject(ContainerId containerId, ObjectId objectId) { + objectService.deleteObject(containerId, objectId); } @Override - public void deleteObjectAsync(ContainerId containerId, ObjectId objectId) { - objectService.deleteObjectAsync(containerId, objectId); + public Iterable searchObjects(ContainerId cid, ObjectFilter... filters) { + return objectService.searchObjects(cid, filters); } @Override - public Iterable searchObjectsAsync(ContainerId cid, ObjectFilter... filters) { - return objectService.searchObjectsAsync(cid, filters); + public NetmapSnapshot getNetmapSnapshot() { + return netmapService.getNetmapSnapshot(); } @Override - public NetmapSnapshot getNetmapSnapshotAsync() { - return netmapService.getNetmapSnapshotAsync(); + public NodeInfo getLocalNodeInfo() { + return netmapService.getLocalNodeInfo(); } @Override - public NodeInfo getLocalNodeInfoAsync() { - return netmapService.getLocalNodeInfoAsync(); + public NetworkSettings getNetworkSettings() { + return netmapService.getNetworkSettings(); } @Override - public NetworkSettings getNetworkSettingsAsync() { - return netmapService.getNetworkSettingsAsync(); + public SessionToken createSession(long expiration) { + return sessionService.createSession(expiration); + } + + public Types.SessionToken createSessionInternal(long expiration) { + return sessionService.createSessionInternal(expiration); } @Override - public SessionToken createSessionAsync(long expiration) { - return sessionService.createSessionAsync(expiration); - } - - public Types.SessionToken createSessionInternalAsync(long expiration) { - return sessionService.createSessionInternalAsync(expiration); - } - - @Override - public ObjectId CalculateObjectId(ObjectHeader header) { - return null; + public ObjectId calculateObjectId(ObjectHeader header) { + return objectTools.calculateObjectId(header); } } diff --git a/client/src/main/java/info/FrostFS/sdk/services/NetmapClient.java b/client/src/main/java/info/FrostFS/sdk/services/NetmapClient.java index f6e9401..80bbf1e 100644 --- a/client/src/main/java/info/FrostFS/sdk/services/NetmapClient.java +++ b/client/src/main/java/info/FrostFS/sdk/services/NetmapClient.java @@ -1,14 +1,13 @@ package info.FrostFS.sdk.services; -import info.FrostFS.sdk.jdo.netmap.NetmapSnapshot; -import info.FrostFS.sdk.jdo.netmap.NodeInfo; +import info.FrostFS.sdk.dto.netmap.NetmapSnapshot; +import info.FrostFS.sdk.dto.netmap.NodeInfo; import info.FrostFS.sdk.tools.NetworkSettings; public interface NetmapClient { + NetmapSnapshot getNetmapSnapshot(); - NetmapSnapshot getNetmapSnapshotAsync(); + NodeInfo getLocalNodeInfo(); - NodeInfo getLocalNodeInfoAsync(); - - NetworkSettings getNetworkSettingsAsync(); + NetworkSettings getNetworkSettings(); } diff --git a/client/src/main/java/info/FrostFS/sdk/services/ObjectClient.java b/client/src/main/java/info/FrostFS/sdk/services/ObjectClient.java index 682d52a..f19fb61 100644 --- a/client/src/main/java/info/FrostFS/sdk/services/ObjectClient.java +++ b/client/src/main/java/info/FrostFS/sdk/services/ObjectClient.java @@ -1,19 +1,20 @@ package info.FrostFS.sdk.services; -import info.FrostFS.sdk.jdo.*; - -import java.io.FileInputStream; +import info.FrostFS.sdk.dto.container.ContainerId; +import info.FrostFS.sdk.tools.PutObjectParameters; +import info.FrostFS.sdk.dto.object.ObjectFilter; +import info.FrostFS.sdk.dto.object.ObjectFrostFS; +import info.FrostFS.sdk.dto.object.ObjectHeader; +import info.FrostFS.sdk.dto.object.ObjectId; public interface ObjectClient { - ObjectHeader getObjectHeadAsync(ContainerId containerId, ObjectId objectId); + ObjectHeader getObjectHead(ContainerId containerId, ObjectId objectId); - ObjectFrostFs getObjectAsync(ContainerId containerId, ObjectId objectId); + ObjectFrostFS getObject(ContainerId containerId, ObjectId objectId); - ObjectId putObjectAsync(ObjectHeader header, FileInputStream payload); + ObjectId putObject(PutObjectParameters parameters); - ObjectId putObjectAsync(ObjectHeader header, byte[] payload); + void deleteObject(ContainerId containerId, ObjectId objectId); - void deleteObjectAsync(ContainerId containerId, ObjectId objectId); - - Iterable searchObjectsAsync(ContainerId cid, ObjectFilter... filters); + Iterable searchObjects(ContainerId cid, ObjectFilter... filters); } diff --git a/client/src/main/java/info/FrostFS/sdk/services/SessionClient.java b/client/src/main/java/info/FrostFS/sdk/services/SessionClient.java index ffd1f0a..6fd7f4f 100644 --- a/client/src/main/java/info/FrostFS/sdk/services/SessionClient.java +++ b/client/src/main/java/info/FrostFS/sdk/services/SessionClient.java @@ -1,8 +1,7 @@ package info.FrostFS.sdk.services; -import info.FrostFS.sdk.jdo.SessionToken; +import info.FrostFS.sdk.dto.SessionToken; public interface SessionClient { - - SessionToken createSessionAsync(long expiration); + SessionToken createSession(long expiration); } diff --git a/client/src/main/java/info/FrostFS/sdk/services/ToolsClient.java b/client/src/main/java/info/FrostFS/sdk/services/ToolsClient.java index 62dffc2..fb673a2 100644 --- a/client/src/main/java/info/FrostFS/sdk/services/ToolsClient.java +++ b/client/src/main/java/info/FrostFS/sdk/services/ToolsClient.java @@ -1,8 +1,8 @@ package info.FrostFS.sdk.services; -import info.FrostFS.sdk.jdo.ObjectHeader; -import info.FrostFS.sdk.jdo.ObjectId; +import info.FrostFS.sdk.dto.object.ObjectHeader; +import info.FrostFS.sdk.dto.object.ObjectId; public interface ToolsClient { - ObjectId CalculateObjectId(ObjectHeader header); + ObjectId calculateObjectId(ObjectHeader header); } diff --git a/client/src/main/java/info/FrostFS/sdk/services/impl/ContainerService.java b/client/src/main/java/info/FrostFS/sdk/services/impl/ContainerService.java index 824f967..4383579 100644 --- a/client/src/main/java/info/FrostFS/sdk/services/impl/ContainerService.java +++ b/client/src/main/java/info/FrostFS/sdk/services/impl/ContainerService.java @@ -3,10 +3,10 @@ package info.FrostFS.sdk.services.impl; import frostFS.container.ContainerServiceGrpc; import frostFS.container.Service; import info.FrostFS.sdk.Verifier; -import info.FrostFS.sdk.jdo.Container; -import info.FrostFS.sdk.jdo.ContainerId; -import info.FrostFS.sdk.mappers.ContainerIdMapper; -import info.FrostFS.sdk.mappers.ContainerMapper; +import info.FrostFS.sdk.dto.container.Container; +import info.FrostFS.sdk.dto.container.ContainerId; +import info.FrostFS.sdk.mappers.container.ContainerIdMapper; +import info.FrostFS.sdk.mappers.container.ContainerMapper; import info.FrostFS.sdk.mappers.OwnerIdMapper; import info.FrostFS.sdk.mappers.VersionMapper; import info.FrostFS.sdk.services.ContainerClient; @@ -21,14 +21,14 @@ import static info.FrostFS.sdk.RequestSigner.sign; import static info.FrostFS.sdk.RequestSigner.signRFC6979; public class ContainerService extends ContextAccessor implements ContainerClient { - private final ContainerServiceGrpc.ContainerServiceBlockingStub containerServiceAsyncClient; + private final ContainerServiceGrpc.ContainerServiceBlockingStub serviceBlockingStub; public ContainerService(ClientEnvironment clientEnvironment) { super(clientEnvironment); - this.containerServiceAsyncClient = ContainerServiceGrpc.newBlockingStub(clientEnvironment.getChannel()); + this.serviceBlockingStub = ContainerServiceGrpc.newBlockingStub(clientEnvironment.getChannel()); } - public Container getContainerAsync(ContainerId cid) { + public Container getContainer(ContainerId cid) { var request = Service.GetRequest.newBuilder() .setBody( Service.GetRequest.Body.newBuilder() @@ -37,26 +37,26 @@ public class ContainerService extends ContextAccessor implements ContainerClient ); addMetaHeader(request); - sign(request, context.getPublicKey(), context.getPrivateKey()); + sign(request, getContext().getKey()); - var response = containerServiceAsyncClient.get(request.build()); + var response = serviceBlockingStub.get(request.build()); Verifier.checkResponse(response); return ContainerMapper.toModel(response.getBody().getContainer()); } - public List listContainersAsync() { + public List listContainers() { var request = Service.ListRequest.newBuilder() .setBody( Service.ListRequest.Body.newBuilder() - .setOwnerId(OwnerIdMapper.toGrpcMessage(context.getOwnerId())) + .setOwnerId(OwnerIdMapper.toGrpcMessage(getContext().getOwnerId())) .build() ); addMetaHeader(request); - sign(request, context.getPublicKey(), context.getPrivateKey()); + sign(request, getContext().getKey()); - var response = containerServiceAsyncClient.list(request.build()); + var response = serviceBlockingStub.list(request.build()); Verifier.checkResponse(response); @@ -65,12 +65,12 @@ public class ContainerService extends ContextAccessor implements ContainerClient .collect(Collectors.toList()); } - public ContainerId createContainerAsync(Container container) { + public ContainerId createContainer(Container container) { var grpcContainer = ContainerMapper.toGrpcMessage(container); grpcContainer = grpcContainer.toBuilder() - .setOwnerId(OwnerIdMapper.toGrpcMessage(context.getOwnerId())) - .setVersion(VersionMapper.toGrpcMessage(context.getVersion())) + .setOwnerId(OwnerIdMapper.toGrpcMessage(getContext().getOwnerId())) + .setVersion(VersionMapper.toGrpcMessage(getContext().getVersion())) .build(); var request = Service.PutRequest.newBuilder() @@ -78,21 +78,21 @@ public class ContainerService extends ContextAccessor implements ContainerClient Service.PutRequest.Body.newBuilder() .setContainer(grpcContainer) .setSignature( - signRFC6979(context.getPublicKey(), context.getPrivateKey(), grpcContainer) + signRFC6979(getContext().getKey(), grpcContainer) ) .build() ); addMetaHeader(request); - sign(request, context.getPublicKey(), context.getPrivateKey()); + sign(request, getContext().getKey()); - var response = containerServiceAsyncClient.put(request.build()); + var response = serviceBlockingStub.put(request.build()); Verifier.checkResponse(response); return ContainerId.fromHash(response.getBody().getContainerId().getValue().toByteArray()); } - public void deleteContainerAsync(ContainerId cid) { + public void deleteContainer(ContainerId cid) { var grpcContainerId = ContainerIdMapper.toGrpcMessage(cid); var request = Service.DeleteRequest.newBuilder() @@ -100,15 +100,15 @@ public class ContainerService extends ContextAccessor implements ContainerClient Service.DeleteRequest.Body.newBuilder() .setContainerId(grpcContainerId) .setSignature(signRFC6979( - context.getPublicKey(), context.getPrivateKey(), grpcContainerId.getValue() + getContext().getKey(), grpcContainerId.getValue() )) .build() ); addMetaHeader(request); - sign(request, context.getPublicKey(), context.getPrivateKey()); + sign(request, getContext().getKey()); - var response = containerServiceAsyncClient.delete(request.build()); + var response = serviceBlockingStub.delete(request.build()); Verifier.checkResponse(response); } diff --git a/client/src/main/java/info/FrostFS/sdk/services/impl/NetmapService.java b/client/src/main/java/info/FrostFS/sdk/services/impl/NetmapService.java index b2ccf23..e55c655 100644 --- a/client/src/main/java/info/FrostFS/sdk/services/impl/NetmapService.java +++ b/client/src/main/java/info/FrostFS/sdk/services/impl/NetmapService.java @@ -4,8 +4,8 @@ import frostFS.netmap.NetmapServiceGrpc; import frostFS.netmap.Service; import frostFS.netmap.Types; import info.FrostFS.sdk.Verifier; -import info.FrostFS.sdk.jdo.netmap.NetmapSnapshot; -import info.FrostFS.sdk.jdo.netmap.NodeInfo; +import info.FrostFS.sdk.dto.netmap.NetmapSnapshot; +import info.FrostFS.sdk.dto.netmap.NodeInfo; import info.FrostFS.sdk.mappers.netmap.NetmapSnapshotMapper; import info.FrostFS.sdk.mappers.netmap.NodeInfoMapper; import info.FrostFS.sdk.services.ContextAccessor; @@ -24,7 +24,7 @@ public class NetmapService extends ContextAccessor implements NetmapClient { public NetmapService(ClientEnvironment clientEnvironment) { super(clientEnvironment); - this.netmapServiceClient = NetmapServiceGrpc.newBlockingStub(context.getChannel()); + this.netmapServiceClient = NetmapServiceGrpc.newBlockingStub(getContext().getChannel()); } private static boolean getBoolValue(byte[] bytes) { @@ -92,12 +92,12 @@ public class NetmapService extends ContextAccessor implements NetmapClient { } @Override - public NetworkSettings getNetworkSettingsAsync() { - if (nonNull(context.getNetworkSettings())) { - return context.getNetworkSettings(); + public NetworkSettings getNetworkSettings() { + if (nonNull(getContext().getNetworkSettings())) { + return getContext().getNetworkSettings(); } - var info = getNetworkInfoAsync(); + var info = getNetworkInfo(); var settings = new NetworkSettings(); @@ -105,18 +105,18 @@ public class NetmapService extends ContextAccessor implements NetmapClient { setNetworksParam(param, settings); } - context.setNetworkSettings(settings); + getContext().setNetworkSettings(settings); return settings; } @Override - public NodeInfo getLocalNodeInfoAsync() { + public NodeInfo getLocalNodeInfo() { var request = Service.LocalNodeInfoRequest.newBuilder() .setBody(Service.LocalNodeInfoRequest.Body.newBuilder().build()); addMetaHeader(request); - sign(request, context.getPublicKey(), context.getPrivateKey()); + sign(request, getContext().getKey()); var response = netmapServiceClient.localNodeInfo(request.build()); Verifier.checkResponse(response); @@ -124,12 +124,12 @@ public class NetmapService extends ContextAccessor implements NetmapClient { return NodeInfoMapper.toModel(response.getBody()); } - public Service.NetworkInfoResponse getNetworkInfoAsync() { + public Service.NetworkInfoResponse getNetworkInfo() { var request = Service.NetworkInfoRequest.newBuilder() .setBody(Service.NetworkInfoRequest.Body.newBuilder().build()); addMetaHeader(request); - sign(request, context.getPublicKey(), context.getPrivateKey()); + sign(request, getContext().getKey()); var response = netmapServiceClient.networkInfo(request.build()); @@ -139,12 +139,12 @@ public class NetmapService extends ContextAccessor implements NetmapClient { } @Override - public NetmapSnapshot getNetmapSnapshotAsync() { + public NetmapSnapshot getNetmapSnapshot() { var request = Service.NetmapSnapshotRequest.newBuilder() .setBody(Service.NetmapSnapshotRequest.Body.newBuilder().build()); addMetaHeader(request); - sign(request, context.getPublicKey(), context.getPrivateKey()); + sign(request, getContext().getKey()); var response = netmapServiceClient.netmapSnapshot(request.build()); diff --git a/client/src/main/java/info/FrostFS/sdk/services/impl/ObjectService.java b/client/src/main/java/info/FrostFS/sdk/services/impl/ObjectService.java index 9ef6a6d..4fbe78d 100644 --- a/client/src/main/java/info/FrostFS/sdk/services/impl/ObjectService.java +++ b/client/src/main/java/info/FrostFS/sdk/services/impl/ObjectService.java @@ -6,16 +6,24 @@ import frostFS.object.ObjectServiceGrpc; import frostFS.object.Service; import frostFS.refs.Types; import info.FrostFS.sdk.Verifier; -import info.FrostFS.sdk.jdo.*; +import info.FrostFS.sdk.constants.AppConst; +import info.FrostFS.sdk.dto.container.ContainerId; +import info.FrostFS.sdk.dto.Split; +import info.FrostFS.sdk.dto.object.*; import info.FrostFS.sdk.mappers.*; +import info.FrostFS.sdk.mappers.container.ContainerIdMapper; +import info.FrostFS.sdk.mappers.object.ObjectFilterMapper; +import info.FrostFS.sdk.mappers.object.ObjectHeaderMapper; +import info.FrostFS.sdk.mappers.object.ObjectIdMapper; +import info.FrostFS.sdk.mappers.object.ObjectFrostFSMapper; import info.FrostFS.sdk.services.ContextAccessor; import info.FrostFS.sdk.services.ObjectClient; import info.FrostFS.sdk.tools.ClientEnvironment; +import info.FrostFS.sdk.tools.PutObjectParameters; +import org.apache.commons.collections4.CollectionUtils; -import java.io.ByteArrayInputStream; import java.io.FileInputStream; import java.io.IOException; -import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -29,15 +37,17 @@ import static java.util.Objects.nonNull; public class ObjectService extends ContextAccessor implements ObjectClient { private final ObjectServiceGrpc.ObjectServiceBlockingStub objectServiceBlockingClient; private final ObjectServiceGrpc.ObjectServiceStub objectServiceClient; + private final ObjectTools objectTools; public ObjectService(ClientEnvironment clientEnvironment) { super(clientEnvironment); - this.objectServiceBlockingClient = ObjectServiceGrpc.newBlockingStub(context.getChannel()); - this.objectServiceClient = ObjectServiceGrpc.newStub(context.getChannel()); + this.objectServiceBlockingClient = ObjectServiceGrpc.newBlockingStub(getContext().getChannel()); + this.objectServiceClient = ObjectServiceGrpc.newStub(getContext().getChannel()); + this.objectTools = new ObjectTools(clientEnvironment); } @Override - public ObjectHeader getObjectHeadAsync(ContainerId cid, ObjectId oid) { + public ObjectHeader getObjectHead(ContainerId cid, ObjectId oid) { var request = Service.HeadRequest.newBuilder() .setBody( Service.HeadRequest.Body.newBuilder() @@ -50,7 +60,7 @@ public class ObjectService extends ContextAccessor implements ObjectClient { ); addMetaHeader(request); - sign(request, context.getPublicKey(), context.getPrivateKey()); + sign(request, getContext().getKey()); var response = objectServiceBlockingClient.head(request.build()); Verifier.checkResponse(response); @@ -59,8 +69,8 @@ public class ObjectService extends ContextAccessor implements ObjectClient { } @Override - public ObjectFrostFs getObjectAsync(ContainerId cid, ObjectId oid) { - var sessionToken = context.getFrostFSClient().createSessionInternalAsync(-1); + public ObjectFrostFS getObject(ContainerId cid, ObjectId oid) { + var sessionToken = getContext().getFrostFSClient().createSessionInternal(-1); var request = Service.GetRequest.newBuilder() .setBody( @@ -71,24 +81,23 @@ public class ObjectService extends ContextAccessor implements ObjectClient { .setObjectId(ObjectIdMapper.toGrpcMessage(oid)) .build() ) - .setRaw(false) .build() ); addMetaHeader(request); addObjectSessionToken( request, sessionToken, ContainerIdMapper.toGrpcMessage(cid), ObjectIdMapper.toGrpcMessage(oid), - frostFS.session.Types.ObjectSessionContext.Verb.GET, context.getPublicKey(), context.getPrivateKey() + frostFS.session.Types.ObjectSessionContext.Verb.GET, getContext().getKey() ); - sign(request, context.getPublicKey(), context.getPrivateKey()); + sign(request, getContext().getKey()); var obj = getObject(request.build()); - - return ObjectMapper.toModel(obj); + return ObjectFrostFSMapper.toModel(obj); } + @Override - public void deleteObjectAsync(ContainerId cid, ObjectId oid) { + public void deleteObject(ContainerId cid, ObjectId oid) { var request = Service.DeleteRequest.newBuilder() .setBody( Service.DeleteRequest.Body.newBuilder() @@ -101,27 +110,17 @@ public class ObjectService extends ContextAccessor implements ObjectClient { .build()); addMetaHeader(request); - sign(request, context.getPublicKey(), context.getPrivateKey()); + sign(request, getContext().getKey()); var response = objectServiceBlockingClient.delete(request.build()); Verifier.checkResponse(response); } @Override - public ObjectId putObjectAsync(ObjectHeader header, FileInputStream payload) { - return putObject(header, payload); - } - - @Override - public ObjectId putObjectAsync(ObjectHeader header, byte[] payload) { - return putObject(header, new ByteArrayInputStream(payload)); - } - - @Override - public Iterable searchObjectsAsync(ContainerId cid, ObjectFilter... filters) { + public Iterable searchObjects(ContainerId cid, ObjectFilter... filters) { var body = Service.SearchRequest.Body.newBuilder() .setContainerId(ContainerIdMapper.toGrpcMessage(cid)) - .setVersion(1); + .setVersion(1);// TODO: clarify this param for (ObjectFilter filter : filters) { body.addFilters(ObjectFilterMapper.toGrpcMessage(filter)); @@ -131,13 +130,43 @@ public class ObjectService extends ContextAccessor implements ObjectClient { .setBody(body.build()); addMetaHeader(request); - sign(request, context.getPublicKey(), context.getPrivateKey()); + sign(request, getContext().getKey()); var objectsIds = searchObjects(request.build()); return Iterables.transform(objectsIds, input -> ObjectId.fromHash(input.getValue().toByteArray())); } + @Override + public ObjectId putObject(PutObjectParameters parameters) { + parameters.validate(); + + return parameters.clientCut ? putClientCutObject(parameters) : putStreamObject(parameters); + } + + public ObjectId putSingleObject(ObjectFrostFS modelObject) { + var sessionToken = getContext().getFrostFSClient().createSessionInternal(-1); + + var grpcObject = objectTools.createObject(modelObject); + + var request = Service.PutSingleRequest.newBuilder() + .setBody(Service.PutSingleRequest.Body.newBuilder().setObject(grpcObject).build()); + + + addMetaHeader(request); + addObjectSessionToken( + request, sessionToken, grpcObject.getHeader().getContainerId(), grpcObject.getObjectId(), + frostFS.session.Types.ObjectSessionContext.Verb.PUT, getContext().getKey() + ); + sign(request, getContext().getKey()); + + var response = objectServiceBlockingClient.putSingle(request.build()); + + Verifier.checkResponse(response); + + return ObjectId.fromHash(grpcObject.getObjectId().getValue().toByteArray()); + } + private frostFS.object.Types.Object getObject(Service.GetRequest request) { var iterator = getObjectInit(request); var obj = iterator.readHeader(); @@ -162,18 +191,20 @@ public class ObjectService extends ContextAccessor implements ObjectClient { return new ObjectReader(objectServiceBlockingClient.get(initRequest)); } - private ObjectId putObject(ObjectHeader header, InputStream payload) { - var sessionToken = context.getFrostFSClient().createSessionInternalAsync(-1); - var hdr = ObjectHeaderMapper.toGrpcMessage(header); + private ObjectId putStreamObject(PutObjectParameters parameters) { + var header = parameters.getHeader(); + var sessionToken = getContext().getFrostFSClient().createSessionInternal(-1); + + var hdr = ObjectHeaderMapper.toGrpcMessage(header); hdr = hdr.toBuilder() - .setOwnerId(OwnerIdMapper.toGrpcMessage(context.getOwnerId())) - .setVersion(VersionMapper.toGrpcMessage(context.getVersion())) + .setOwnerId(OwnerIdMapper.toGrpcMessage(getContext().getOwnerId())) + .setVersion(VersionMapper.toGrpcMessage(getContext().getVersion())) .build(); var oid = Types.ObjectID.newBuilder().setValue(getSha256(hdr)).build(); - var request = Service.PutRequest.newBuilder() + var initRequest = Service.PutRequest.newBuilder() .setBody( Service.PutRequest.Body.newBuilder() .setInit( @@ -181,34 +212,35 @@ public class ObjectService extends ContextAccessor implements ObjectClient { ).build() ); - addMetaHeader(request); + addMetaHeader(initRequest); addObjectSessionToken( - request, sessionToken, hdr.getContainerId(), oid, frostFS.session.Types.ObjectSessionContext.Verb.PUT, - context.getPublicKey(), context.getPrivateKey() + initRequest, sessionToken, hdr.getContainerId(), oid, + frostFS.session.Types.ObjectSessionContext.Verb.PUT, getContext().getKey() ); - sign(request, context.getPublicKey(), context.getPrivateKey()); + sign(initRequest, getContext().getKey()); - var writer = putObjectInit(request.build()); + var writer = putObjectInit(initRequest.build()); - var buffer = new byte[Constants.OBJECT_CHUNK_SIZE]; - int bufferLength = 0; - try { - bufferLength = payload.readNBytes(buffer, 0, Constants.OBJECT_CHUNK_SIZE); - while (bufferLength > 0) { - request.setBody( - Service.PutRequest.Body.newBuilder() - .setChunk(ByteString.copyFrom(Arrays.copyOfRange(buffer, 0, bufferLength))) - .build() - ) - .clearVerifyHeader(); - sign(request, context.getPublicKey(), context.getPrivateKey()); - writer.write(request.build()); - bufferLength = payload.readNBytes(buffer, 0, Constants.OBJECT_CHUNK_SIZE); - } - } catch ( - IOException e) { - throw new RuntimeException(e); + var bufferSize = parameters.getBufferMaxSize() > 0 ? parameters.getBufferMaxSize() : AppConst.OBJECT_CHUNK_SIZE; + bufferSize = (int) Math.min(getStreamSize(parameters.getPayload()), bufferSize); + bufferSize = header.getPayloadLength() > 0 ? (int) Math.min(header.getPayloadLength(), bufferSize) : bufferSize; + + var buffer = new byte[bufferSize]; + while (true) { + var bytesCount = readNBytes(parameters.getPayload(), buffer, bufferSize); + if (bytesCount <= 0) + break; + + var chunkRequest = Service.PutRequest.newBuilder(initRequest.build()) + .setBody( + Service.PutRequest.Body.newBuilder() + .setChunk(ByteString.copyFrom(Arrays.copyOfRange(buffer, 0, bytesCount))) + .build() + ) + .clearVerifyHeader(); + sign(chunkRequest, getContext().getKey()); + writer.write(chunkRequest.build()); } var response = writer.complete(); @@ -217,6 +249,71 @@ public class ObjectService extends ContextAccessor implements ObjectClient { return ObjectId.fromHash(response.getBody().getObjectId().getValue().toByteArray()); } + private ObjectId putClientCutObject(PutObjectParameters parameters) { + var header = parameters.getHeader(); + + var networkSettings = getContext().getFrostFSClient().getNetworkSettings(); + var payloadSize = getStreamSize(parameters.getPayload()); + var objectSize = (int) Math.min(payloadSize, networkSettings.getMaxObjectSize()); + var fullLength = header.getPayloadLength() == 0 ? payloadSize : header.getPayloadLength(); + if (fullLength == 0) { + throw new IllegalArgumentException("Payload stream must be able to seek or PayloadLength must be specified"); + } + + var buffer = new byte[objectSize]; + var largeObject = new LargeObject(header.getContainerId()); + var split = new Split(); + + ObjectId objectId; + List sentObjectIds = new ArrayList<>(); + ObjectFrostFS currentObject; + + while (true) { + var bytesCount = readNBytes(parameters.getPayload(), buffer, objectSize); + if (CollectionUtils.isNotEmpty(sentObjectIds)) { + split.setPrevious(sentObjectIds.get(sentObjectIds.size() - 1)); + } + + largeObject.appendBlock(buffer, bytesCount); + + currentObject = new ObjectFrostFS( + header.getContainerId(), + bytesCount < objectSize ? Arrays.copyOfRange(buffer, 0, bytesCount) : buffer + ); + currentObject.setSplit(split); + + if (largeObject.getPayloadLength() == fullLength) + break; + + objectId = putSingleObject(currentObject); + + sentObjectIds.add(objectId); + } + + if (CollectionUtils.isEmpty(sentObjectIds)) { + currentObject.addAttributes(parameters.getHeader().getAttributes()); + return putSingleObject(currentObject); + } + + largeObject.addAttributes(parameters.getHeader().getAttributes()); + largeObject.calculateHash(); + + currentObject.setParent(largeObject); + + objectId = putSingleObject(currentObject); + + sentObjectIds.add(objectId); + + var linkObject = new LinkObject(header.getContainerId(), split.getSplitId(), largeObject); + linkObject.addChildren(sentObjectIds); + + linkObject.getHeader().getAttributes().clear(); + + putSingleObject(linkObject); + + return objectTools.calculateObjectId(largeObject.getHeader()); + } + private ObjectWriter putObjectInit(Service.PutRequest initRequest) { if (initRequest.getSerializedSize() == 0) { throw new IllegalArgumentException(initRequest.getClass().getName()); @@ -248,4 +345,19 @@ public class ObjectService extends ContextAccessor implements ObjectClient { return new SearchReader(objectServiceBlockingClient.search(initRequest)); } + private int readNBytes(FileInputStream fileInputStream, byte[] buffer, int size) { + try { + return fileInputStream.readNBytes(buffer, 0, size); + } catch (IOException exp) { + throw new IllegalArgumentException(exp.getMessage()); + } + } + + private long getStreamSize(FileInputStream fileInputStream) { + try { + return fileInputStream.getChannel().size(); + } catch (IOException exp) { + throw new IllegalArgumentException(exp.getMessage()); + } + } } diff --git a/client/src/main/java/info/FrostFS/sdk/services/impl/ObjectTools.java b/client/src/main/java/info/FrostFS/sdk/services/impl/ObjectTools.java new file mode 100644 index 0000000..31fbebd --- /dev/null +++ b/client/src/main/java/info/FrostFS/sdk/services/impl/ObjectTools.java @@ -0,0 +1,106 @@ +package info.FrostFS.sdk.services.impl; + +import com.google.protobuf.ByteString; +import frostFS.object.Types; +import info.FrostFS.sdk.dto.object.ObjectFrostFS; +import info.FrostFS.sdk.dto.object.ObjectHeader; +import info.FrostFS.sdk.dto.object.ObjectId; +import info.FrostFS.sdk.mappers.object.ObjectHeaderMapper; +import info.FrostFS.sdk.mappers.object.ObjectIdMapper; +import info.FrostFS.sdk.mappers.OwnerIdMapper; +import info.FrostFS.sdk.mappers.VersionMapper; +import info.FrostFS.sdk.services.ContextAccessor; +import info.FrostFS.sdk.services.ToolsClient; +import info.FrostFS.sdk.tools.ClientEnvironment; +import org.apache.commons.collections4.ListUtils; + +import static info.FrostFS.sdk.Helper.getSha256; +import static info.FrostFS.sdk.RequestSigner.signData; +import static java.util.Objects.nonNull; + +public class ObjectTools extends ContextAccessor implements ToolsClient { + public ObjectTools(ClientEnvironment context) { + super(context); + } + + private static frostFS.refs.Types.Checksum sha256Checksum(byte[] data) { + return frostFS.refs.Types.Checksum.newBuilder() + .setType(frostFS.refs.Types.ChecksumType.SHA256) + .setSum(ByteString.copyFrom(getSha256(data))) + .build(); + } + + @Override + public ObjectId calculateObjectId(ObjectHeader header) { + var grpcHeader = createHeader(header, new byte[]{}); + + return ObjectIdMapper.toModel( + frostFS.refs.Types.ObjectID.newBuilder().setValue(getSha256(grpcHeader)).build() + ); + } + + public Types.Object createObject(ObjectFrostFS objectFrostFs) { + var grpcHeaderBuilder = ObjectHeaderMapper.toGrpcMessage(objectFrostFs.getHeader()).toBuilder() + .setOwnerId(OwnerIdMapper.toGrpcMessage(getContext().getOwnerId())) + .setVersion(VersionMapper.toGrpcMessage(getContext().getVersion())) + .setPayloadLength(objectFrostFs.getPayload().length) + .setPayloadHash(sha256Checksum(objectFrostFs.getPayload())); + + var split = objectFrostFs.getHeader().getSplit(); + if (nonNull(split)) { + var splitGrpc = Types.Header.Split.newBuilder() + .setSplitId(nonNull(split.getSplitId()) ? ByteString.copyFrom(split.getSplitId().toBinary()) : null); + + ListUtils.emptyIfNull(split.getChildren()).stream() + .map(ObjectIdMapper::toGrpcMessage) + .forEach(splitGrpc::addChildren); + + + if (nonNull(split.getParentHeader())) { + var grpcParentHeader = createHeader(split.getParentHeader(), new byte[]{}); + var parent = frostFS.refs.Types.ObjectID.newBuilder().setValue(getSha256(grpcParentHeader)).build(); + + splitGrpc + .setParent(parent) + .setParentHeader(grpcParentHeader) + .setParentSignature( + frostFS.refs.Types.Signature.newBuilder() + .setKey(ByteString.copyFrom(getContext().getKey().getPublicKeyByte())) + .setSign(ByteString.copyFrom(signData(getContext().getKey(), parent.toByteArray()))) + ); + + split.setParent(ObjectIdMapper.toModel(parent)); + } + if (nonNull(split.getPrevious())) { + splitGrpc.setPrevious(ObjectIdMapper.toGrpcMessage(split.getPrevious())); + } + grpcHeaderBuilder.setSplit(splitGrpc); + } + + var grpcHeader = grpcHeaderBuilder.build(); + var objectId = frostFS.refs.Types.ObjectID.newBuilder().setValue(getSha256(grpcHeader)).build(); + return Types.Object.newBuilder() + .setHeader(grpcHeader) + .setObjectId(objectId) + .setPayload(ByteString.copyFrom(objectFrostFs.getPayload())) + .setSignature( + frostFS.refs.Types.Signature.newBuilder() + .setKey(ByteString.copyFrom(getContext().getKey().getPublicKeyByte())) + .setSign(ByteString.copyFrom(signData(getContext().getKey(), objectId.toByteArray()))) + ) + .build(); + } + + private Types.Header createHeader(ObjectHeader header, byte[] payload) { + var grpcHeader = ObjectHeaderMapper.toGrpcMessage(header).toBuilder() + .setOwnerId(OwnerIdMapper.toGrpcMessage(getContext().getOwnerId())) + .setVersion(VersionMapper.toGrpcMessage(getContext().getVersion())); + + if (header.getPayloadCheckSum() != null) + grpcHeader.setPayloadHash(sha256Checksum(header.getPayloadCheckSum())); + else if (payload != null) + grpcHeader.setPayloadHash(sha256Checksum(payload)); + + return grpcHeader.build(); + } +} diff --git a/client/src/main/java/info/FrostFS/sdk/services/impl/SessionService.java b/client/src/main/java/info/FrostFS/sdk/services/impl/SessionService.java index 3406acf..cae3cb6 100644 --- a/client/src/main/java/info/FrostFS/sdk/services/impl/SessionService.java +++ b/client/src/main/java/info/FrostFS/sdk/services/impl/SessionService.java @@ -3,7 +3,7 @@ package info.FrostFS.sdk.services.impl; import frostFS.session.Service; import frostFS.session.SessionServiceGrpc; import frostFS.session.Types; -import info.FrostFS.sdk.jdo.SessionToken; +import info.FrostFS.sdk.dto.SessionToken; import info.FrostFS.sdk.mappers.OwnerIdMapper; import info.FrostFS.sdk.mappers.SessionMapper; import info.FrostFS.sdk.services.ContextAccessor; @@ -14,36 +14,36 @@ import static info.FrostFS.sdk.RequestConstructor.addMetaHeader; import static info.FrostFS.sdk.RequestSigner.sign; public class SessionService extends ContextAccessor implements SessionClient { - private final SessionServiceGrpc.SessionServiceBlockingStub sessionServiceAsyncClient; + private final SessionServiceGrpc.SessionServiceBlockingStub serviceBlockingStub; public SessionService(ClientEnvironment clientEnvironment) { super(clientEnvironment); - this.sessionServiceAsyncClient = SessionServiceGrpc.newBlockingStub(context.getChannel()); + this.serviceBlockingStub = SessionServiceGrpc.newBlockingStub(getContext().getChannel()); } @Override - public SessionToken createSessionAsync(long expiration) { - var sessionToken = createSessionInternalAsync(expiration); + public SessionToken createSession(long expiration) { + var sessionToken = createSessionInternal(expiration); var token = SessionMapper.serialize(sessionToken); return new SessionToken(new byte[]{}, token); } - public Types.SessionToken createSessionInternalAsync(long expiration) { + public Types.SessionToken createSessionInternal(long expiration) { var request = Service.CreateRequest.newBuilder() .setBody( Service.CreateRequest.Body.newBuilder() - .setOwnerId(OwnerIdMapper.toGrpcMessage(context.getOwnerId())) + .setOwnerId(OwnerIdMapper.toGrpcMessage(getContext().getOwnerId())) .setExpiration(expiration).build() ); addMetaHeader(request); - sign(request, context.getPublicKey(), context.getPrivateKey()); + sign(request, getContext().getKey()); return createSession(request.build()); } private Types.SessionToken createSession(Service.CreateRequest request) { - var resp = sessionServiceAsyncClient.create(request); + var resp = serviceBlockingStub.create(request); var lifetime = Types.SessionToken.Body.TokenLifetime.newBuilder() .setExp(request.getBody().getExpiration()) diff --git a/client/src/main/java/info/FrostFS/sdk/tools/ClientEnvironment.java b/client/src/main/java/info/FrostFS/sdk/tools/ClientEnvironment.java index 93019ca..ee1d8d4 100644 --- a/client/src/main/java/info/FrostFS/sdk/tools/ClientEnvironment.java +++ b/client/src/main/java/info/FrostFS/sdk/tools/ClientEnvironment.java @@ -1,26 +1,21 @@ package info.FrostFS.sdk.tools; -import info.FrostFS.sdk.jdo.OwnerId; -import info.FrostFS.sdk.jdo.Version; +import info.FrostFS.sdk.dto.OwnerId; +import info.FrostFS.sdk.dto.Version; import info.FrostFS.sdk.services.FrostFSClient; import io.grpc.Channel; -import static info.FrostFS.sdk.KeyExtension.getPrivateKeyFromWIF; -import static info.FrostFS.sdk.KeyExtension.loadPublicKey; - public class ClientEnvironment { private final OwnerId ownerId; private final Version version; - private final byte[] privateKey; - private final byte[] publicKey; + private final ECDsa key; private final Channel channel; private final FrostFSClient frostFSClient; private NetworkSettings networkSettings; - public ClientEnvironment(String key, Channel channel, Version version, FrostFSClient frostFSClient) { - this.privateKey = getPrivateKeyFromWIF(key); - this.publicKey = loadPublicKey(privateKey); - this.ownerId = OwnerId.fromKey(publicKey); + public ClientEnvironment(String wif, Channel channel, Version version, FrostFSClient frostFSClient) { + this.key = new ECDsa(wif); + this.ownerId = OwnerId.fromKey(key.getPublicKeyByte()); this.version = version; this.channel = channel; this.frostFSClient = frostFSClient; @@ -50,11 +45,7 @@ public class ClientEnvironment { return version; } - public byte[] getPrivateKey() { - return privateKey; - } - - public byte[] getPublicKey() { - return publicKey; + public ECDsa getKey() { + return key; } } diff --git a/client/src/main/java/info/FrostFS/sdk/ClientSettings.java b/client/src/main/java/info/FrostFS/sdk/tools/ClientSettings.java similarity index 97% rename from client/src/main/java/info/FrostFS/sdk/ClientSettings.java rename to client/src/main/java/info/FrostFS/sdk/tools/ClientSettings.java index 3b09497..2b25083 100644 --- a/client/src/main/java/info/FrostFS/sdk/ClientSettings.java +++ b/client/src/main/java/info/FrostFS/sdk/tools/ClientSettings.java @@ -1,4 +1,4 @@ -package info.FrostFS.sdk; +package info.FrostFS.sdk.tools; import io.grpc.ChannelCredentials; import org.apache.commons.lang3.StringUtils; diff --git a/client/src/main/java/info/FrostFS/sdk/tools/ECDsa.java b/client/src/main/java/info/FrostFS/sdk/tools/ECDsa.java new file mode 100644 index 0000000..98ccfc0 --- /dev/null +++ b/client/src/main/java/info/FrostFS/sdk/tools/ECDsa.java @@ -0,0 +1,29 @@ +package info.FrostFS.sdk.tools; + +import java.security.PrivateKey; + +import static info.FrostFS.sdk.KeyExtension.*; + +public class ECDsa { + private final byte[] publicKeyByte; + private final byte[] privateKeyByte; + private final PrivateKey privateKey; + + public ECDsa(String wif) { + this.privateKeyByte = getPrivateKeyFromWIF(wif); + this.publicKeyByte = loadPublicKey(privateKeyByte); + this.privateKey = loadPrivateKey(privateKeyByte); + } + + public byte[] getPublicKeyByte() { + return publicKeyByte; + } + + public byte[] getPrivateKeyByte() { + return privateKeyByte; + } + + public PrivateKey getPrivateKey() { + return privateKey; + } +} diff --git a/client/src/main/java/info/FrostFS/sdk/tools/PutObjectParameters.java b/client/src/main/java/info/FrostFS/sdk/tools/PutObjectParameters.java new file mode 100644 index 0000000..ca62737 --- /dev/null +++ b/client/src/main/java/info/FrostFS/sdk/tools/PutObjectParameters.java @@ -0,0 +1,76 @@ +package info.FrostFS.sdk.tools; + +import info.FrostFS.sdk.dto.object.ObjectHeader; + +import java.io.FileInputStream; + +import static java.util.Objects.isNull; + +public class PutObjectParameters { + private static final String ERROR_TEMPLATE = "%s value cannot be null."; + + public ObjectHeader header; + public FileInputStream payload; + public boolean clientCut; + public int bufferMaxSize; + + public PutObjectParameters(ObjectHeader header, FileInputStream payload, boolean clientCut, int bufferMaxSize) { + this.header = header; + this.payload = payload; + this.clientCut = clientCut; + this.bufferMaxSize = bufferMaxSize; + } + + public PutObjectParameters(ObjectHeader header, FileInputStream payload) { + this.header = header; + this.payload = payload; + } + + public ObjectHeader getHeader() { + return header; + } + + public void setHeader(ObjectHeader header) { + this.header = header; + } + + public FileInputStream getPayload() { + return payload; + } + + public void setPayload(FileInputStream payload) { + this.payload = payload; + } + + public boolean isClientCut() { + return clientCut; + } + + public void setClientCut(boolean clientCut) { + this.clientCut = clientCut; + } + + public int getBufferMaxSize() { + return bufferMaxSize; + } + + public void setBufferMaxSize(int bufferMaxSize) { + this.bufferMaxSize = bufferMaxSize; + } + + public void validate() { + StringBuilder errorMessage = new StringBuilder(); + + if (isNull(header)) { + errorMessage.append(String.format(ERROR_TEMPLATE, "Header")).append(System.lineSeparator()); + } + + if (isNull(payload)) { + errorMessage.append(String.format(ERROR_TEMPLATE, "Payload")).append(System.lineSeparator()); + } + + if (errorMessage.length() != 0) { + throw new IllegalArgumentException(errorMessage.toString()); + } + } +} diff --git a/cryptography/pom.xml b/cryptography/pom.xml index 9af2880..9c18d69 100644 --- a/cryptography/pom.xml +++ b/cryptography/pom.xml @@ -19,11 +19,6 @@ - - commons-codec - commons-codec - 1.17.0 - com.google.protobuf protobuf-java @@ -34,11 +29,6 @@ bcprov-jdk18on 1.78.1 - - org.bouncycastle - bcpkix-jdk18on - 1.78.1 - \ No newline at end of file diff --git a/cryptography/src/main/java/info/FrostFS/sdk/Helper.java b/cryptography/src/main/java/info/FrostFS/sdk/Helper.java index e4e1460..fb11ac2 100644 --- a/cryptography/src/main/java/info/FrostFS/sdk/Helper.java +++ b/cryptography/src/main/java/info/FrostFS/sdk/Helper.java @@ -18,19 +18,23 @@ public class Helper { return hash; } - public static byte[] getSha256(byte[] value) { + public static MessageDigest getSha256Instance() { try { - return MessageDigest.getInstance("SHA-256").digest(value); + return MessageDigest.getInstance("SHA-256"); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } } + public static byte[] getSha256(byte[] value) { + return getSha256Instance().digest(value); + } + public static ByteString getSha256(AbstractMessage value) { return ByteString.copyFrom(getSha256(value.toByteArray())); } - public static String пуеHexString(byte[] array) { + public static String getHexString(byte[] array) { return String.format("%0" + (array.length << 1) + "x", new BigInteger(1, array)); } } diff --git a/cryptography/src/main/java/info/FrostFS/sdk/KeyExtension.java b/cryptography/src/main/java/info/FrostFS/sdk/KeyExtension.java index b8c5ab6..d057a04 100644 --- a/cryptography/src/main/java/info/FrostFS/sdk/KeyExtension.java +++ b/cryptography/src/main/java/info/FrostFS/sdk/KeyExtension.java @@ -57,7 +57,9 @@ public class KeyExtension { public static byte[] loadPublicKey(byte[] privateKey) { X9ECParameters params = SECNamedCurves.getByOID(SECObjectIdentifiers.secp256r1); - ECDomainParameters domain = new ECDomainParameters(params.getCurve(), params.getG(), params.getN(), params.getH()); + ECDomainParameters domain = new ECDomainParameters( + params.getCurve(), params.getG(), params.getN(), params.getH() + ); ECPoint q = domain.getG().multiply(new BigInteger(1, privateKey)); ECPublicKeyParameters publicParams = new ECPublicKeyParameters(q, domain); return publicParams.getQ().getEncoded(true); @@ -65,7 +67,9 @@ public class KeyExtension { public static PrivateKey loadPrivateKey(byte[] privateKey) { X9ECParameters params = SECNamedCurves.getByOID(SECObjectIdentifiers.secp256r1); - ECDomainParameters domain = new ECDomainParameters(params.getCurve(), params.getG(), params.getN(), params.getH()); + ECDomainParameters domain = new ECDomainParameters( + params.getCurve(), params.getG(), params.getN(), params.getH() + ); ECPrivateKeyParameters ecParams = new ECPrivateKeyParameters(fromUnsignedByteArray(privateKey), domain); ECParameterSpec ecParameterSpec = new ECNamedCurveSpec( @@ -80,7 +84,7 @@ public class KeyExtension { } } - public static PublicKey getPublicKeyByPublic(byte[] publicKey) { + public static PublicKey getPublicKeyFromBytes(byte[] publicKey) { if (publicKey.length != COMPRESSED_PUBLIC_KEY_LENGTH) { throw new IllegalArgumentException( String.format("Decompress argument isn't compressed public key. Expected length=%s, actual=%s", @@ -89,14 +93,17 @@ public class KeyExtension { } X9ECParameters secp256R1 = SECNamedCurves.getByOID(SECObjectIdentifiers.secp256r1); - ECDomainParameters domain = new ECDomainParameters(secp256R1.getCurve(), secp256R1.getG(), secp256R1.getN(), secp256R1.getH()); + ECDomainParameters domain = new ECDomainParameters( + secp256R1.getCurve(), secp256R1.getG(), secp256R1.getN(), secp256R1.getH() + ); var ecPoint = secp256R1.getCurve().decodePoint(publicKey); var publicParams = new ECPublicKeyParameters(ecPoint, domain); java.security.spec.ECPoint point = new java.security.spec.ECPoint( publicParams.getQ().getRawXCoord().toBigInteger(), publicParams.getQ().getRawYCoord().toBigInteger() ); ECParameterSpec ecParameterSpec = new ECNamedCurveSpec( - "secp256r1", secp256R1.getCurve(), secp256R1.getG(), secp256R1.getN(), secp256R1.getH(), secp256R1.getSeed() + "secp256r1", secp256R1.getCurve(), secp256R1.getG(), secp256R1.getN(), + secp256R1.getH(), secp256R1.getSeed() ); ECPublicKeySpec publicKeySpec = new ECPublicKeySpec(point, ecParameterSpec); @@ -108,7 +115,6 @@ public class KeyExtension { } } - public static byte[] getScriptHash(byte[] publicKey) { var script = createSignatureRedeemScript(publicKey); diff --git a/modelsV2/pom.xml b/models/pom.xml similarity index 93% rename from modelsV2/pom.xml rename to models/pom.xml index 9093e71..7f9af60 100644 --- a/modelsV2/pom.xml +++ b/models/pom.xml @@ -9,7 +9,7 @@ 0.1.0 - modelsV2 + models 11 @@ -30,7 +30,7 @@ info.FrostFS.sdk - protosV2 + protos 0.1.0 diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/UUIDExtension.java b/models/src/main/java/info/FrostFS/sdk/UUIDExtension.java similarity index 100% rename from modelsV2/src/main/java/info/FrostFS/sdk/UUIDExtension.java rename to models/src/main/java/info/FrostFS/sdk/UUIDExtension.java diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/Constants.java b/models/src/main/java/info/FrostFS/sdk/constants/AppConst.java similarity index 66% rename from modelsV2/src/main/java/info/FrostFS/sdk/jdo/Constants.java rename to models/src/main/java/info/FrostFS/sdk/constants/AppConst.java index 2c01d9e..8880eb0 100644 --- a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/Constants.java +++ b/models/src/main/java/info/FrostFS/sdk/constants/AppConst.java @@ -1,6 +1,6 @@ -package info.FrostFS.sdk.jdo; +package info.FrostFS.sdk.constants; -public class Constants { +public class AppConst { public static final int OBJECT_CHUNK_SIZE = 3 * (1 << 20); public static final int SHA256_HASH_LENGTH = 32; } diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/constants/XHeaderConst.java b/models/src/main/java/info/FrostFS/sdk/constants/XHeaderConst.java similarity index 100% rename from modelsV2/src/main/java/info/FrostFS/sdk/constants/XHeaderConst.java rename to models/src/main/java/info/FrostFS/sdk/constants/XHeaderConst.java diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/MetaHeader.java b/models/src/main/java/info/FrostFS/sdk/dto/MetaHeader.java similarity index 86% rename from modelsV2/src/main/java/info/FrostFS/sdk/jdo/MetaHeader.java rename to models/src/main/java/info/FrostFS/sdk/dto/MetaHeader.java index 5f428b4..b0eead0 100644 --- a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/MetaHeader.java +++ b/models/src/main/java/info/FrostFS/sdk/dto/MetaHeader.java @@ -1,9 +1,9 @@ -package info.FrostFS.sdk.jdo; +package info.FrostFS.sdk.dto; public class MetaHeader { - public Version version; - public int epoch; - public int ttl; + private Version version; + private int epoch; + private int ttl; public MetaHeader(Version version, int epoch, int ttl) { this.version = version; diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/OwnerId.java b/models/src/main/java/info/FrostFS/sdk/dto/OwnerId.java similarity index 94% rename from modelsV2/src/main/java/info/FrostFS/sdk/jdo/OwnerId.java rename to models/src/main/java/info/FrostFS/sdk/dto/OwnerId.java index 723e234..40b4f04 100644 --- a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/OwnerId.java +++ b/models/src/main/java/info/FrostFS/sdk/dto/OwnerId.java @@ -1,4 +1,4 @@ -package info.FrostFS.sdk.jdo; +package info.FrostFS.sdk.dto; import info.FrostFS.sdk.Base58; diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/SessionToken.java b/models/src/main/java/info/FrostFS/sdk/dto/SessionToken.java similarity index 74% rename from modelsV2/src/main/java/info/FrostFS/sdk/jdo/SessionToken.java rename to models/src/main/java/info/FrostFS/sdk/dto/SessionToken.java index bd4f7b0..5980875 100644 --- a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/SessionToken.java +++ b/models/src/main/java/info/FrostFS/sdk/dto/SessionToken.java @@ -1,8 +1,8 @@ -package info.FrostFS.sdk.jdo; +package info.FrostFS.sdk.dto; public class SessionToken { - public byte[] id; - public byte[] sessionKey; + private final byte[] id; + private final byte[] sessionKey; public SessionToken(byte[] id, byte[] sessionKey) { this.id = id; diff --git a/models/src/main/java/info/FrostFS/sdk/dto/Signature.java b/models/src/main/java/info/FrostFS/sdk/dto/Signature.java new file mode 100644 index 0000000..0ab5b3e --- /dev/null +++ b/models/src/main/java/info/FrostFS/sdk/dto/Signature.java @@ -0,0 +1,33 @@ +package info.FrostFS.sdk.dto; + +import info.FrostFS.sdk.enums.SignatureScheme; + +public class Signature { + private byte[] key; + private byte[] sign; + private SignatureScheme scheme; + + public byte[] getKey() { + return key; + } + + public void setKey(byte[] key) { + this.key = key; + } + + public byte[] getSign() { + return sign; + } + + public void setSign(byte[] sign) { + this.sign = sign; + } + + public SignatureScheme getScheme() { + return scheme; + } + + public void setScheme(SignatureScheme scheme) { + this.scheme = scheme; + } +} diff --git a/models/src/main/java/info/FrostFS/sdk/dto/Split.java b/models/src/main/java/info/FrostFS/sdk/dto/Split.java new file mode 100644 index 0000000..be51331 --- /dev/null +++ b/models/src/main/java/info/FrostFS/sdk/dto/Split.java @@ -0,0 +1,69 @@ +package info.FrostFS.sdk.dto; + +import info.FrostFS.sdk.dto.object.ObjectHeader; +import info.FrostFS.sdk.dto.object.ObjectId; + +import java.util.ArrayList; +import java.util.List; + +public class Split { + private final List children; + private SplitId splitId; + private ObjectId parent; + private ObjectId previous; + private Signature parentSignature; + private ObjectHeader parentHeader; + + public Split() { + this(new SplitId()); + } + + public Split(SplitId splitId) { + this.splitId = splitId; + this.children = new ArrayList<>(); + } + + public SplitId getSplitId() { + return splitId; + } + + private void setSplitId(SplitId splitId) { + this.splitId = splitId; + } + + public ObjectId getParent() { + return parent; + } + + public void setParent(ObjectId parent) { + this.parent = parent; + } + + public ObjectId getPrevious() { + return previous; + } + + public void setPrevious(ObjectId previous) { + this.previous = previous; + } + + public Signature getParentSignature() { + return parentSignature; + } + + public void setParentSignature(Signature parentSignature) { + this.parentSignature = parentSignature; + } + + public ObjectHeader getParentHeader() { + return parentHeader; + } + + public void setParentHeader(ObjectHeader parentHeader) { + this.parentHeader = parentHeader; + } + + public List getChildren() { + return children; + } +} diff --git a/models/src/main/java/info/FrostFS/sdk/dto/SplitId.java b/models/src/main/java/info/FrostFS/sdk/dto/SplitId.java new file mode 100644 index 0000000..b01b8d0 --- /dev/null +++ b/models/src/main/java/info/FrostFS/sdk/dto/SplitId.java @@ -0,0 +1,44 @@ +package info.FrostFS.sdk.dto; + +import java.util.UUID; + +import static info.FrostFS.sdk.UUIDExtension.asBytes; +import static info.FrostFS.sdk.UUIDExtension.asUuid; +import static java.util.Objects.isNull; + +public class SplitId { + private final UUID id; + + public SplitId(UUID uuid) { + this.id = uuid; + } + + public SplitId() { + this.id = UUID.randomUUID(); + } + + private SplitId(byte[] binary) { + this.id = asUuid(binary); + } + + private SplitId(String str) { + this.id = UUID.fromString(str); + } + + public static SplitId createFromBinary(byte[] binaryData) { + return new SplitId(binaryData); + } + + public static SplitId createFromString(String stringData) { + return new SplitId(stringData); + } + + @Override + public String toString() { + return id.toString(); + } + + public byte[] toBinary() { + return isNull(id) ? null : asBytes(id); + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/Status.java b/models/src/main/java/info/FrostFS/sdk/dto/Status.java similarity index 71% rename from modelsV2/src/main/java/info/FrostFS/sdk/jdo/Status.java rename to models/src/main/java/info/FrostFS/sdk/dto/Status.java index bcb6f08..d766546 100644 --- a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/Status.java +++ b/models/src/main/java/info/FrostFS/sdk/dto/Status.java @@ -1,16 +1,16 @@ -package info.FrostFS.sdk.jdo; +package info.FrostFS.sdk.dto; import info.FrostFS.sdk.enums.StatusCode; -import java.util.Optional; +import static java.util.Objects.isNull; public class Status { - public StatusCode code; - public String message; + private StatusCode code; + private String message; public Status(StatusCode code, String message) { this.code = code; - this.message = Optional.ofNullable(message).orElse(""); + this.message = isNull(message) ? "" : message; } public Status(StatusCode code) { @@ -36,7 +36,7 @@ public class Status { @Override public String toString() { - return "Response status: " + code + ". Message: " + message + "."; + return String.format("Response status: %s. Message: %s.", code, message); } public boolean isSuccess() { diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/Version.java b/models/src/main/java/info/FrostFS/sdk/dto/Version.java similarity index 88% rename from modelsV2/src/main/java/info/FrostFS/sdk/jdo/Version.java rename to models/src/main/java/info/FrostFS/sdk/dto/Version.java index 22cd8c7..c498b49 100644 --- a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/Version.java +++ b/models/src/main/java/info/FrostFS/sdk/dto/Version.java @@ -1,8 +1,13 @@ -package info.FrostFS.sdk.jdo; +package info.FrostFS.sdk.dto; public class Version { - public int major; - public int minor; + private int major; + private int minor; + + public Version(int major, int minor) { + this.major = major; + this.minor = minor; + } public int getMajor() { return major; @@ -25,11 +30,6 @@ public class Version { return "v" + major + "." + minor; } - public Version(int major, int minor) { - this.major = major; - this.minor = minor; - } - public boolean isSupported(Version version) { return major == version.getMajor(); } diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/Container.java b/models/src/main/java/info/FrostFS/sdk/dto/container/Container.java similarity index 64% rename from modelsV2/src/main/java/info/FrostFS/sdk/jdo/Container.java rename to models/src/main/java/info/FrostFS/sdk/dto/container/Container.java index 38df197..b952fcf 100644 --- a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/Container.java +++ b/models/src/main/java/info/FrostFS/sdk/dto/container/Container.java @@ -1,15 +1,22 @@ -package info.FrostFS.sdk.jdo; +package info.FrostFS.sdk.dto.container; +import info.FrostFS.sdk.dto.Version; +import info.FrostFS.sdk.dto.netmap.PlacementPolicy; import info.FrostFS.sdk.enums.BasicAcl; -import info.FrostFS.sdk.jdo.netmap.PlacementPolicy; import java.util.UUID; public class Container { - public UUID nonce; - public BasicAcl basicAcl; - public PlacementPolicy placementPolicy; - public Version version; + private UUID nonce; + private BasicAcl basicAcl; + private PlacementPolicy placementPolicy; + private Version version; + + public Container(BasicAcl basicAcl, PlacementPolicy placementPolicy) { + this.nonce = UUID.randomUUID(); + this.basicAcl = basicAcl; + this.placementPolicy = placementPolicy; + } public UUID getNonce() { return nonce; @@ -42,17 +49,4 @@ public class Container { public void setVersion(Version version) { this.version = version; } - - public Container(BasicAcl basicAcl, PlacementPolicy placementPolicy) { - nonce = UUID.randomUUID(); - this.basicAcl = basicAcl; - this.placementPolicy = placementPolicy; - } - - public Container(BasicAcl basicAcl, PlacementPolicy placementPolicy, UUID nonce, Version version) { - this.nonce = nonce; - this.basicAcl = basicAcl; - this.placementPolicy = placementPolicy; - this.version = version; - } } diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/ContainerId.java b/models/src/main/java/info/FrostFS/sdk/dto/container/ContainerId.java similarity index 78% rename from modelsV2/src/main/java/info/FrostFS/sdk/jdo/ContainerId.java rename to models/src/main/java/info/FrostFS/sdk/dto/container/ContainerId.java index 309f25b..fd8d529 100644 --- a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/ContainerId.java +++ b/models/src/main/java/info/FrostFS/sdk/dto/container/ContainerId.java @@ -1,16 +1,17 @@ -package info.FrostFS.sdk.jdo; +package info.FrostFS.sdk.dto.container; import info.FrostFS.sdk.Base58; +import info.FrostFS.sdk.constants.AppConst; public class ContainerId { - public String value; + private String value; public ContainerId(String value) { this.value = value; } public static ContainerId fromHash(byte[] hash) { - if (hash.length != Constants.SHA256_HASH_LENGTH) { + if (hash.length != AppConst.SHA256_HASH_LENGTH) { throw new IllegalArgumentException("ContainerID must be a sha256 hash."); } return new ContainerId(Base58.encode(hash)); diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/netmap/NetmapSnapshot.java b/models/src/main/java/info/FrostFS/sdk/dto/netmap/NetmapSnapshot.java similarity index 75% rename from modelsV2/src/main/java/info/FrostFS/sdk/jdo/netmap/NetmapSnapshot.java rename to models/src/main/java/info/FrostFS/sdk/dto/netmap/NetmapSnapshot.java index 7fd344e..8619f00 100644 --- a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/netmap/NetmapSnapshot.java +++ b/models/src/main/java/info/FrostFS/sdk/dto/netmap/NetmapSnapshot.java @@ -1,10 +1,10 @@ -package info.FrostFS.sdk.jdo.netmap; +package info.FrostFS.sdk.dto.netmap; import java.util.List; public class NetmapSnapshot { - public Long epoch; - public List nodeInfoCollection; + private final Long epoch; + private final List nodeInfoCollection; public NetmapSnapshot(Long epoch, List nodeInfoCollection) { this.epoch = epoch; diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/netmap/NodeInfo.java b/models/src/main/java/info/FrostFS/sdk/dto/netmap/NodeInfo.java similarity index 66% rename from modelsV2/src/main/java/info/FrostFS/sdk/jdo/netmap/NodeInfo.java rename to models/src/main/java/info/FrostFS/sdk/dto/netmap/NodeInfo.java index 5b060f1..bd78146 100644 --- a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/netmap/NodeInfo.java +++ b/models/src/main/java/info/FrostFS/sdk/dto/netmap/NodeInfo.java @@ -1,17 +1,17 @@ -package info.FrostFS.sdk.jdo.netmap; +package info.FrostFS.sdk.dto.netmap; +import info.FrostFS.sdk.dto.Version; import info.FrostFS.sdk.enums.NodeState; -import info.FrostFS.sdk.jdo.Version; import java.util.List; import java.util.Map; public class NodeInfo { - public NodeState state; - public Version version; - public List addresses; - public Map attributes; - public byte[] publicKey; + private final NodeState state; + private final Version version; + private final List addresses; + private final Map attributes; + private final byte[] publicKey; public NodeInfo(NodeState state, Version version, List addresses, Map attributes, byte[] publicKey) { @@ -26,18 +26,10 @@ public class NodeInfo { return state; } - public void setState(NodeState state) { - this.state = state; - } - public Version getVersion() { return version; } - public void setVersion(Version version) { - this.version = version; - } - public byte[] getPublicKey() { return publicKey; } diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/netmap/PlacementPolicy.java b/models/src/main/java/info/FrostFS/sdk/dto/netmap/PlacementPolicy.java similarity index 52% rename from modelsV2/src/main/java/info/FrostFS/sdk/jdo/netmap/PlacementPolicy.java rename to models/src/main/java/info/FrostFS/sdk/dto/netmap/PlacementPolicy.java index d32b585..9e92a63 100644 --- a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/netmap/PlacementPolicy.java +++ b/models/src/main/java/info/FrostFS/sdk/dto/netmap/PlacementPolicy.java @@ -1,8 +1,8 @@ -package info.FrostFS.sdk.jdo.netmap; +package info.FrostFS.sdk.dto.netmap; public class PlacementPolicy { - public Replica[] replicas; - public boolean unique; + private final Replica[] replicas; + private final boolean unique; public PlacementPolicy(boolean unique, Replica[] replicas) { this.replicas = replicas; @@ -13,15 +13,7 @@ public class PlacementPolicy { return replicas; } - public void setReplicas(Replica[] replicas) { - this.replicas = replicas; - } - public boolean isUnique() { return unique; } - - public void setUnique(boolean unique) { - this.unique = unique; - } } diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/netmap/Replica.java b/models/src/main/java/info/FrostFS/sdk/dto/netmap/Replica.java similarity index 72% rename from modelsV2/src/main/java/info/FrostFS/sdk/jdo/netmap/Replica.java rename to models/src/main/java/info/FrostFS/sdk/dto/netmap/Replica.java index 80d1e89..5189df7 100644 --- a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/netmap/Replica.java +++ b/models/src/main/java/info/FrostFS/sdk/dto/netmap/Replica.java @@ -1,14 +1,14 @@ -package info.FrostFS.sdk.jdo.netmap; +package info.FrostFS.sdk.dto.netmap; -import java.util.Optional; +import static java.util.Objects.isNull; public class Replica { - public int count; - public String selector; + private int count; + private String selector; public Replica(int count, String selector) { this.count = count; - this.selector = Optional.ofNullable(selector).orElse(""); + this.selector = isNull(selector) ? "" : selector; } public Replica(int count) { diff --git a/models/src/main/java/info/FrostFS/sdk/dto/object/LargeObject.java b/models/src/main/java/info/FrostFS/sdk/dto/object/LargeObject.java new file mode 100644 index 0000000..66d9ad0 --- /dev/null +++ b/models/src/main/java/info/FrostFS/sdk/dto/object/LargeObject.java @@ -0,0 +1,29 @@ +package info.FrostFS.sdk.dto.object; + +import info.FrostFS.sdk.dto.container.ContainerId; + +import java.security.MessageDigest; + +import static info.FrostFS.sdk.Helper.getSha256Instance; + +public class LargeObject extends ObjectFrostFS { + private final MessageDigest payloadHash; + + public LargeObject(ContainerId cid) { + super(cid, new byte[]{}); + this.payloadHash = getSha256Instance(); + } + + public void appendBlock(byte[] bytes, int count) { + this.getHeader().increasePayloadLength(count); + this.payloadHash.update(bytes, 0, count); + } + + public void calculateHash() { + this.getHeader().setPayloadCheckSum(this.payloadHash.digest()); + } + + public long getPayloadLength() { + return getHeader().getPayloadLength(); + } +} diff --git a/models/src/main/java/info/FrostFS/sdk/dto/object/LinkObject.java b/models/src/main/java/info/FrostFS/sdk/dto/object/LinkObject.java new file mode 100644 index 0000000..e7b8ea4 --- /dev/null +++ b/models/src/main/java/info/FrostFS/sdk/dto/object/LinkObject.java @@ -0,0 +1,21 @@ +package info.FrostFS.sdk.dto.object; + +import info.FrostFS.sdk.dto.Split; +import info.FrostFS.sdk.dto.SplitId; +import info.FrostFS.sdk.dto.container.ContainerId; + +import java.util.List; + +public class LinkObject extends ObjectFrostFS { + + public LinkObject(ContainerId cid, SplitId splitId, LargeObject largeObject) { + super(cid, new byte[]{}); + var split = new Split(splitId); + split.setParentHeader(largeObject.getHeader()); + this.getHeader().setSplit(split); + } + + public void addChildren(List objectIds) { + this.getHeader().getSplit().getChildren().addAll(objectIds); + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/ObjectAttribute.java b/models/src/main/java/info/FrostFS/sdk/dto/object/ObjectAttribute.java similarity index 82% rename from modelsV2/src/main/java/info/FrostFS/sdk/jdo/ObjectAttribute.java rename to models/src/main/java/info/FrostFS/sdk/dto/object/ObjectAttribute.java index 1a122f4..9729f01 100644 --- a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/ObjectAttribute.java +++ b/models/src/main/java/info/FrostFS/sdk/dto/object/ObjectAttribute.java @@ -1,8 +1,8 @@ -package info.FrostFS.sdk.jdo; +package info.FrostFS.sdk.dto.object; public class ObjectAttribute { - public String key; - public String value; + private String key; + private String value; public ObjectAttribute(String key, String value) { this.key = key; diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/ObjectFilter.java b/models/src/main/java/info/FrostFS/sdk/dto/object/ObjectFilter.java similarity index 82% rename from modelsV2/src/main/java/info/FrostFS/sdk/jdo/ObjectFilter.java rename to models/src/main/java/info/FrostFS/sdk/dto/object/ObjectFilter.java index 0fb1db3..0dae796 100644 --- a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/ObjectFilter.java +++ b/models/src/main/java/info/FrostFS/sdk/dto/object/ObjectFilter.java @@ -1,13 +1,15 @@ -package info.FrostFS.sdk.jdo; +package info.FrostFS.sdk.dto.object; +import info.FrostFS.sdk.dto.OwnerId; +import info.FrostFS.sdk.dto.Version; import info.FrostFS.sdk.enums.ObjectMatchType; public class ObjectFilter { - private static final String HEADER_PREFIX = "Object:"; + private static final String HEADER_PREFIX = "$Object:"; - public ObjectMatchType matchType; - public String key; - public String value; + private ObjectMatchType matchType; + private String key; + private String value; public ObjectFilter(ObjectMatchType matchType, String key, String value) { @@ -17,7 +19,7 @@ public class ObjectFilter { } public static ObjectFilter ObjectIdFilter(ObjectMatchType matchType, ObjectId objectId) { - return new ObjectFilter(matchType, HEADER_PREFIX + "objectID", objectId.value); + return new ObjectFilter(matchType, HEADER_PREFIX + "objectID", objectId.getValue()); } public static ObjectFilter OwnerFilter(ObjectMatchType matchType, OwnerId ownerId) { diff --git a/models/src/main/java/info/FrostFS/sdk/dto/object/ObjectFrostFS.java b/models/src/main/java/info/FrostFS/sdk/dto/object/ObjectFrostFS.java new file mode 100644 index 0000000..3b9d3c9 --- /dev/null +++ b/models/src/main/java/info/FrostFS/sdk/dto/object/ObjectFrostFS.java @@ -0,0 +1,80 @@ +package info.FrostFS.sdk.dto.object; + +import info.FrostFS.sdk.dto.Split; +import info.FrostFS.sdk.dto.container.ContainerId; +import info.FrostFS.sdk.enums.ObjectType; + +import java.util.ArrayList; +import java.util.List; + +import static java.util.Objects.isNull; + +public class ObjectFrostFS { + private ObjectHeader header; + private ObjectId objectId; + private byte[] payload; + + public ObjectFrostFS(ObjectHeader header, ObjectId objectId, byte[] payload) { + this.header = header; + this.objectId = objectId; + this.payload = payload; + } + + public ObjectFrostFS(ContainerId container, byte[] payload) { + this.payload = payload; + this.header = new ObjectHeader(container, new ArrayList<>()); + } + + public ObjectFrostFS(ContainerId container, byte[] payload, ObjectType objectType) { + this.payload = payload; + this.header = new ObjectHeader(container, objectType, new ArrayList<>()); + } + + public ObjectHeader getHeader() { + return header; + } + + public void setHeader(ObjectHeader header) { + this.header = header; + } + + public ObjectId getObjectId() { + return objectId; + } + + private void setObjectId(ObjectId objectId) { + this.objectId = objectId; + } + + public byte[] getPayload() { + return payload; + } + + public void setPayload(byte[] payload) { + this.payload = payload; + } + + public void setSplit(Split split) { + header.setSplit(split); + } + + public void addAttribute(String key, String value) { + header.getAttributes().add(new ObjectAttribute(key, value)); + } + + public void addAttribute(ObjectAttribute attribute) { + header.getAttributes().add(attribute); + } + + public void addAttributes(List attributes) { + header.getAttributes().addAll(attributes); + } + + public void setParent(LargeObject largeObject) { + if (isNull(header.getSplit())) { + throw new IllegalArgumentException("The object is not initialized properly"); + } + + header.getSplit().setParentHeader(largeObject.getHeader()); + } +} diff --git a/models/src/main/java/info/FrostFS/sdk/dto/object/ObjectHeader.java b/models/src/main/java/info/FrostFS/sdk/dto/object/ObjectHeader.java new file mode 100644 index 0000000..401ed41 --- /dev/null +++ b/models/src/main/java/info/FrostFS/sdk/dto/object/ObjectHeader.java @@ -0,0 +1,118 @@ +package info.FrostFS.sdk.dto.object; + +import info.FrostFS.sdk.dto.OwnerId; +import info.FrostFS.sdk.dto.Split; +import info.FrostFS.sdk.dto.Version; +import info.FrostFS.sdk.dto.container.ContainerId; +import info.FrostFS.sdk.enums.ObjectType; + +import java.util.List; + +public class ObjectHeader { + private List attributes; + private ContainerId containerId; + private long size; + private ObjectType objectType; + private Version version; + private OwnerId ownerId; + private long payloadLength; + private byte[] payloadCheckSum; + private Split split; + + public ObjectHeader(ContainerId containerId, ObjectType objectType, + List attributes, long size, Version version) { + this.attributes = attributes; + this.containerId = containerId; + this.size = size; + this.objectType = objectType; + this.version = version; + } + + public ObjectHeader(ContainerId containerId, ObjectType type, List attributes) { + this.attributes = attributes; + this.containerId = containerId; + this.objectType = type; + } + + public ObjectHeader(ContainerId containerId, List attributes) { + this.attributes = attributes; + this.containerId = containerId; + this.objectType = ObjectType.REGULAR; + } + + public OwnerId getOwnerId() { + return ownerId; + } + + public void setOwnerId(OwnerId ownerId) { + this.ownerId = ownerId; + } + + public long getPayloadLength() { + return payloadLength; + } + + public void setPayloadLength(long payloadLength) { + this.payloadLength = payloadLength; + } + + public void increasePayloadLength(long payloadLength) { + this.payloadLength += payloadLength; + } + + public byte[] getPayloadCheckSum() { + return payloadCheckSum; + } + + public void setPayloadCheckSum(byte[] payloadCheckSum) { + this.payloadCheckSum = payloadCheckSum; + } + + public Split getSplit() { + return split; + } + + public void setSplit(Split split) { + this.split = split; + } + + public List getAttributes() { + return attributes; + } + + public void setAttributes(List attributes) { + this.attributes = attributes; + } + + public ContainerId getContainerId() { + return containerId; + } + + public void setContainerId(ContainerId containerId) { + this.containerId = containerId; + } + + public long getSize() { + return size; + } + + public void setSize(long size) { + this.size = size; + } + + public ObjectType getObjectType() { + return objectType; + } + + public void setObjectType(ObjectType objectType) { + this.objectType = objectType; + } + + public Version getVersion() { + return version; + } + + public void setVersion(Version version) { + this.version = version; + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/ObjectId.java b/models/src/main/java/info/FrostFS/sdk/dto/object/ObjectId.java similarity index 75% rename from modelsV2/src/main/java/info/FrostFS/sdk/jdo/ObjectId.java rename to models/src/main/java/info/FrostFS/sdk/dto/object/ObjectId.java index 438d027..4b36d80 100644 --- a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/ObjectId.java +++ b/models/src/main/java/info/FrostFS/sdk/dto/object/ObjectId.java @@ -1,16 +1,17 @@ -package info.FrostFS.sdk.jdo; +package info.FrostFS.sdk.dto.object; import info.FrostFS.sdk.Base58; +import info.FrostFS.sdk.constants.AppConst; public class ObjectId { - public String value; + private final String value; public ObjectId(String id) { this.value = id; } public static ObjectId fromHash(byte[] hash) { - if (hash.length != Constants.SHA256_HASH_LENGTH) { + if (hash.length != AppConst.SHA256_HASH_LENGTH) { throw new IllegalArgumentException("ObjectId must be a sha256 hash."); } return new ObjectId(Base58.encode(hash)); diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/enums/BasicAcl.java b/models/src/main/java/info/FrostFS/sdk/enums/BasicAcl.java similarity index 93% rename from modelsV2/src/main/java/info/FrostFS/sdk/enums/BasicAcl.java rename to models/src/main/java/info/FrostFS/sdk/enums/BasicAcl.java index b454a68..7ebf7f7 100644 --- a/modelsV2/src/main/java/info/FrostFS/sdk/enums/BasicAcl.java +++ b/models/src/main/java/info/FrostFS/sdk/enums/BasicAcl.java @@ -28,6 +28,6 @@ public enum BasicAcl { } public static BasicAcl get(int value) { - return ENUM_MAP_BY_VALUE.get(value); + return ENUM_MAP_BY_VALUE.get(value); } } diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/enums/NodeState.java b/models/src/main/java/info/FrostFS/sdk/enums/NodeState.java similarity index 100% rename from modelsV2/src/main/java/info/FrostFS/sdk/enums/NodeState.java rename to models/src/main/java/info/FrostFS/sdk/enums/NodeState.java diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/enums/ObjectMatchType.java b/models/src/main/java/info/FrostFS/sdk/enums/ObjectMatchType.java similarity index 100% rename from modelsV2/src/main/java/info/FrostFS/sdk/enums/ObjectMatchType.java rename to models/src/main/java/info/FrostFS/sdk/enums/ObjectMatchType.java diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/enums/ObjectType.java b/models/src/main/java/info/FrostFS/sdk/enums/ObjectType.java similarity index 100% rename from modelsV2/src/main/java/info/FrostFS/sdk/enums/ObjectType.java rename to models/src/main/java/info/FrostFS/sdk/enums/ObjectType.java diff --git a/models/src/main/java/info/FrostFS/sdk/enums/SignatureScheme.java b/models/src/main/java/info/FrostFS/sdk/enums/SignatureScheme.java new file mode 100644 index 0000000..362c75a --- /dev/null +++ b/models/src/main/java/info/FrostFS/sdk/enums/SignatureScheme.java @@ -0,0 +1,14 @@ +package info.FrostFS.sdk.enums; + +public enum SignatureScheme { + ECDSA_SHA512(0), + ECDSA_RFC6979_SHA256(1), + ECDSA_RFC6979_SHA256_WALLET_CONNECT(2), + ; + + public final int value; + + SignatureScheme(int value) { + this.value = value; + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/enums/StatusCode.java b/models/src/main/java/info/FrostFS/sdk/enums/StatusCode.java similarity index 100% rename from modelsV2/src/main/java/info/FrostFS/sdk/enums/StatusCode.java rename to models/src/main/java/info/FrostFS/sdk/enums/StatusCode.java diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/MetaHeaderMapper.java b/models/src/main/java/info/FrostFS/sdk/mappers/MetaHeaderMapper.java similarity index 91% rename from modelsV2/src/main/java/info/FrostFS/sdk/mappers/MetaHeaderMapper.java rename to models/src/main/java/info/FrostFS/sdk/mappers/MetaHeaderMapper.java index 2d493c4..96371d7 100644 --- a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/MetaHeaderMapper.java +++ b/models/src/main/java/info/FrostFS/sdk/mappers/MetaHeaderMapper.java @@ -1,7 +1,7 @@ package info.FrostFS.sdk.mappers; import frostFS.session.Types; -import info.FrostFS.sdk.jdo.MetaHeader; +import info.FrostFS.sdk.dto.MetaHeader; public class MetaHeaderMapper { diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/OwnerIdMapper.java b/models/src/main/java/info/FrostFS/sdk/mappers/OwnerIdMapper.java similarity index 89% rename from modelsV2/src/main/java/info/FrostFS/sdk/mappers/OwnerIdMapper.java rename to models/src/main/java/info/FrostFS/sdk/mappers/OwnerIdMapper.java index bc7027c..edf9f1c 100644 --- a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/OwnerIdMapper.java +++ b/models/src/main/java/info/FrostFS/sdk/mappers/OwnerIdMapper.java @@ -2,9 +2,10 @@ package info.FrostFS.sdk.mappers; import com.google.protobuf.ByteString; import frostFS.refs.Types; -import info.FrostFS.sdk.jdo.OwnerId; +import info.FrostFS.sdk.dto.OwnerId; public class OwnerIdMapper { + public static Types.OwnerID toGrpcMessage(OwnerId ownerId) { return Types.OwnerID.newBuilder() .setValue(ByteString.copyFrom(ownerId.toHash())) diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/SessionMapper.java b/models/src/main/java/info/FrostFS/sdk/mappers/SessionMapper.java similarity index 73% rename from modelsV2/src/main/java/info/FrostFS/sdk/mappers/SessionMapper.java rename to models/src/main/java/info/FrostFS/sdk/mappers/SessionMapper.java index f9d7ecd..a8d329d 100644 --- a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/SessionMapper.java +++ b/models/src/main/java/info/FrostFS/sdk/mappers/SessionMapper.java @@ -8,24 +8,22 @@ import java.io.IOException; public class SessionMapper { - public static byte[] serialize(Types.SessionToken token) - { + public static byte[] serialize(Types.SessionToken token) { try { byte[] bytes = new byte[token.getSerializedSize()]; CodedOutputStream stream = CodedOutputStream.newInstance(bytes); token.writeTo(stream); return bytes; - } catch (IOException e) { - throw new IllegalArgumentException(e); + } catch (IOException exp) { + throw new IllegalArgumentException(exp.getMessage()); } } - public static Types.SessionToken deserializeSessionToken(byte[] bytes) - { + public static Types.SessionToken deserializeSessionToken(byte[] bytes) { try { return Types.SessionToken.newBuilder().mergeFrom(bytes).build(); } catch (InvalidProtocolBufferException exp) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException(exp.getMessage()); } } } diff --git a/models/src/main/java/info/FrostFS/sdk/mappers/SignatureMapper.java b/models/src/main/java/info/FrostFS/sdk/mappers/SignatureMapper.java new file mode 100644 index 0000000..3a388d0 --- /dev/null +++ b/models/src/main/java/info/FrostFS/sdk/mappers/SignatureMapper.java @@ -0,0 +1,25 @@ +package info.FrostFS.sdk.mappers; + +import com.google.protobuf.ByteString; +import frostFS.refs.Types; +import info.FrostFS.sdk.dto.Signature; + +import static java.util.Objects.isNull; + +public class SignatureMapper { + + public static Types.Signature ToGrpcMessage(Signature signature) { + var scheme = Types.SignatureScheme.forNumber(signature.getScheme().value); + if (isNull(scheme)) { + throw new IllegalArgumentException( + String.format("Unknown SignatureScheme. Value: %s.", signature.getScheme().name()) + ); + } + + return Types.Signature.newBuilder() + .setKey(ByteString.copyFrom(signature.getKey())) + .setSign(ByteString.copyFrom(signature.getSign())) + .setScheme(scheme) + .build(); + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/StatusMapper.java b/models/src/main/java/info/FrostFS/sdk/mappers/StatusMapper.java similarity index 94% rename from modelsV2/src/main/java/info/FrostFS/sdk/mappers/StatusMapper.java rename to models/src/main/java/info/FrostFS/sdk/mappers/StatusMapper.java index 6cb68d7..3817f7d 100644 --- a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/StatusMapper.java +++ b/models/src/main/java/info/FrostFS/sdk/mappers/StatusMapper.java @@ -1,12 +1,13 @@ package info.FrostFS.sdk.mappers; import frostFS.status.Types; +import info.FrostFS.sdk.dto.Status; import info.FrostFS.sdk.enums.StatusCode; -import info.FrostFS.sdk.jdo.Status; import static java.util.Objects.isNull; public class StatusMapper { + public static Status toModel(Types.Status status) { if (isNull(status)) return new Status(StatusCode.SUCCESS); @@ -15,7 +16,6 @@ public class StatusMapper { throw new IllegalArgumentException( String.format("Unknown StatusCode. Value: %s.", status.getCode()) ); - } return new Status(statusCode, status.getMessage()); diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/VersionMapper.java b/models/src/main/java/info/FrostFS/sdk/mappers/VersionMapper.java similarity index 92% rename from modelsV2/src/main/java/info/FrostFS/sdk/mappers/VersionMapper.java rename to models/src/main/java/info/FrostFS/sdk/mappers/VersionMapper.java index 339746a..fefb7ba 100644 --- a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/VersionMapper.java +++ b/models/src/main/java/info/FrostFS/sdk/mappers/VersionMapper.java @@ -1,7 +1,7 @@ package info.FrostFS.sdk.mappers; import frostFS.refs.Types; -import info.FrostFS.sdk.jdo.Version; +import info.FrostFS.sdk.dto.Version; public class VersionMapper { diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/ContainerIdMapper.java b/models/src/main/java/info/FrostFS/sdk/mappers/container/ContainerIdMapper.java similarity index 73% rename from modelsV2/src/main/java/info/FrostFS/sdk/mappers/ContainerIdMapper.java rename to models/src/main/java/info/FrostFS/sdk/mappers/container/ContainerIdMapper.java index bab5b61..b661e71 100644 --- a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/ContainerIdMapper.java +++ b/models/src/main/java/info/FrostFS/sdk/mappers/container/ContainerIdMapper.java @@ -1,14 +1,12 @@ -package info.FrostFS.sdk.mappers; +package info.FrostFS.sdk.mappers.container; import com.google.protobuf.ByteString; import frostFS.refs.Types; -import info.FrostFS.sdk.jdo.ContainerId; +import info.FrostFS.sdk.dto.container.ContainerId; public class ContainerIdMapper { public static Types.ContainerID toGrpcMessage(ContainerId containerId) { - var test = containerId.toHash(); - return Types.ContainerID.newBuilder() .setValue(ByteString.copyFrom(containerId.toHash())) .build(); diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/ContainerMapper.java b/models/src/main/java/info/FrostFS/sdk/mappers/container/ContainerMapper.java similarity index 60% rename from modelsV2/src/main/java/info/FrostFS/sdk/mappers/ContainerMapper.java rename to models/src/main/java/info/FrostFS/sdk/mappers/container/ContainerMapper.java index 4f1d886..9895eee 100644 --- a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/ContainerMapper.java +++ b/models/src/main/java/info/FrostFS/sdk/mappers/container/ContainerMapper.java @@ -1,10 +1,10 @@ -package info.FrostFS.sdk.mappers; - +package info.FrostFS.sdk.mappers.container; import com.google.protobuf.ByteString; import frostFS.container.Types; +import info.FrostFS.sdk.dto.container.Container; import info.FrostFS.sdk.enums.BasicAcl; -import info.FrostFS.sdk.jdo.Container; +import info.FrostFS.sdk.mappers.VersionMapper; import info.FrostFS.sdk.mappers.netmap.PlacementPolicyMapper; import static info.FrostFS.sdk.UUIDExtension.asBytes; @@ -21,19 +21,17 @@ public class ContainerMapper { .build(); } - public static Container toModel(Types.Container container) { - var basicAcl = BasicAcl.get(container.getBasicAcl()); + public static Container toModel(Types.Container containerGrpc) { + var basicAcl = BasicAcl.get(containerGrpc.getBasicAcl()); if (isNull(basicAcl)) { throw new IllegalArgumentException( - String.format("Unknown BasicACL rule. Value: %s.", container.getBasicAcl()) + String.format("Unknown BasicACL rule. Value: %s.", containerGrpc.getBasicAcl()) ); } - return new Container( - basicAcl, - PlacementPolicyMapper.toModel(container.getPlacementPolicy()), - asUuid(container.getNonce().toByteArray()), - VersionMapper.toModel(container.getVersion()) - ); + var container = new Container(basicAcl, PlacementPolicyMapper.toModel(containerGrpc.getPlacementPolicy())); + container.setNonce(asUuid(containerGrpc.getNonce().toByteArray())); + container.setVersion(VersionMapper.toModel(containerGrpc.getVersion())); + return container; } } diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/netmap/NetmapSnapshotMapper.java b/models/src/main/java/info/FrostFS/sdk/mappers/netmap/NetmapSnapshotMapper.java similarity index 74% rename from modelsV2/src/main/java/info/FrostFS/sdk/mappers/netmap/NetmapSnapshotMapper.java rename to models/src/main/java/info/FrostFS/sdk/mappers/netmap/NetmapSnapshotMapper.java index 9d9eb27..d18fc32 100644 --- a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/netmap/NetmapSnapshotMapper.java +++ b/models/src/main/java/info/FrostFS/sdk/mappers/netmap/NetmapSnapshotMapper.java @@ -1,7 +1,7 @@ package info.FrostFS.sdk.mappers.netmap; import frostFS.netmap.Service; -import info.FrostFS.sdk.jdo.netmap.NetmapSnapshot; +import info.FrostFS.sdk.dto.netmap.NetmapSnapshot; import java.util.stream.Collectors; @@ -11,7 +11,7 @@ public class NetmapSnapshotMapper { return new NetmapSnapshot( netmap.getBody().getNetmap().getEpoch(), netmap.getBody().getNetmap().getNodesList().stream() - .map(t -> NodeInfoMapper.toModel(t, netmap.getMetaHeader().getVersion())) + .map(node -> NodeInfoMapper.toModel(node, netmap.getMetaHeader().getVersion())) .collect(Collectors.toList()) ); } diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/netmap/NodeInfoMapper.java b/models/src/main/java/info/FrostFS/sdk/mappers/netmap/NodeInfoMapper.java similarity index 96% rename from modelsV2/src/main/java/info/FrostFS/sdk/mappers/netmap/NodeInfoMapper.java rename to models/src/main/java/info/FrostFS/sdk/mappers/netmap/NodeInfoMapper.java index 6c9e25c..53eca35 100644 --- a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/netmap/NodeInfoMapper.java +++ b/models/src/main/java/info/FrostFS/sdk/mappers/netmap/NodeInfoMapper.java @@ -3,8 +3,8 @@ package info.FrostFS.sdk.mappers.netmap; import frostFS.netmap.Service; import frostFS.netmap.Types.NodeInfo.Attribute; import frostFS.refs.Types; +import info.FrostFS.sdk.dto.netmap.NodeInfo; import info.FrostFS.sdk.enums.NodeState; -import info.FrostFS.sdk.jdo.netmap.NodeInfo; import info.FrostFS.sdk.mappers.VersionMapper; import java.util.stream.Collectors; @@ -12,13 +12,13 @@ import java.util.stream.Collectors; import static java.util.Objects.isNull; public class NodeInfoMapper { + public static NodeInfo toModel(Service.LocalNodeInfoResponse.Body nodeInfo) { return toModel(nodeInfo.getNodeInfo(), nodeInfo.getVersion()); } public static NodeInfo toModel(frostFS.netmap.Types.NodeInfo nodeInfo, Types.Version version) { NodeState nodeState = NodeState.get(nodeInfo.getState().getNumber()); - if (isNull(nodeState)) { throw new IllegalArgumentException( String.format("Unknown NodeState. Value: %s.", nodeInfo.getState()) diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/netmap/PlacementPolicyMapper.java b/models/src/main/java/info/FrostFS/sdk/mappers/netmap/PlacementPolicyMapper.java similarity index 88% rename from modelsV2/src/main/java/info/FrostFS/sdk/mappers/netmap/PlacementPolicyMapper.java rename to models/src/main/java/info/FrostFS/sdk/mappers/netmap/PlacementPolicyMapper.java index 871dfcb..e2803d6 100644 --- a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/netmap/PlacementPolicyMapper.java +++ b/models/src/main/java/info/FrostFS/sdk/mappers/netmap/PlacementPolicyMapper.java @@ -1,10 +1,11 @@ package info.FrostFS.sdk.mappers.netmap; import frostFS.netmap.Types; -import info.FrostFS.sdk.jdo.netmap.PlacementPolicy; -import info.FrostFS.sdk.jdo.netmap.Replica; +import info.FrostFS.sdk.dto.netmap.PlacementPolicy; +import info.FrostFS.sdk.dto.netmap.Replica; public class PlacementPolicyMapper { + public static Types.PlacementPolicy toGrpcMessage(PlacementPolicy placementPolicy) { var pp = Types.PlacementPolicy.newBuilder() .setUnique(placementPolicy.isUnique()); diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/netmap/ReplicaMapper.java b/models/src/main/java/info/FrostFS/sdk/mappers/netmap/ReplicaMapper.java similarity index 91% rename from modelsV2/src/main/java/info/FrostFS/sdk/mappers/netmap/ReplicaMapper.java rename to models/src/main/java/info/FrostFS/sdk/mappers/netmap/ReplicaMapper.java index 93735ff..b2929ba 100644 --- a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/netmap/ReplicaMapper.java +++ b/models/src/main/java/info/FrostFS/sdk/mappers/netmap/ReplicaMapper.java @@ -1,9 +1,10 @@ package info.FrostFS.sdk.mappers.netmap; import frostFS.netmap.Types; -import info.FrostFS.sdk.jdo.netmap.Replica; +import info.FrostFS.sdk.dto.netmap.Replica; public class ReplicaMapper { + public static Types.Replica toGrpcMessage(Replica replica) { return Types.Replica.newBuilder() .setCount(replica.getCount()) diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/ObjectAttributeMapper.java b/models/src/main/java/info/FrostFS/sdk/mappers/object/ObjectAttributeMapper.java similarity index 83% rename from modelsV2/src/main/java/info/FrostFS/sdk/mappers/ObjectAttributeMapper.java rename to models/src/main/java/info/FrostFS/sdk/mappers/object/ObjectAttributeMapper.java index 45489a5..7604e0f 100644 --- a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/ObjectAttributeMapper.java +++ b/models/src/main/java/info/FrostFS/sdk/mappers/object/ObjectAttributeMapper.java @@ -1,9 +1,10 @@ -package info.FrostFS.sdk.mappers; +package info.FrostFS.sdk.mappers.object; import frostFS.object.Types; -import info.FrostFS.sdk.jdo.ObjectAttribute; +import info.FrostFS.sdk.dto.object.ObjectAttribute; public class ObjectAttributeMapper { + public static Types.Header.Attribute toGrpcMessage(ObjectAttribute attribute) { return Types.Header.Attribute.newBuilder() .setKey(attribute.getKey()) diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/ObjectFilterMapper.java b/models/src/main/java/info/FrostFS/sdk/mappers/object/ObjectFilterMapper.java similarity index 86% rename from modelsV2/src/main/java/info/FrostFS/sdk/mappers/ObjectFilterMapper.java rename to models/src/main/java/info/FrostFS/sdk/mappers/object/ObjectFilterMapper.java index 328de81..52adf8d 100644 --- a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/ObjectFilterMapper.java +++ b/models/src/main/java/info/FrostFS/sdk/mappers/object/ObjectFilterMapper.java @@ -1,13 +1,13 @@ -package info.FrostFS.sdk.mappers; +package info.FrostFS.sdk.mappers.object; import frostFS.object.Service; import frostFS.object.Types; -import info.FrostFS.sdk.jdo.ObjectFilter; -import org.apache.commons.lang3.EnumUtils; +import info.FrostFS.sdk.dto.object.ObjectFilter; import static java.util.Objects.isNull; public class ObjectFilterMapper { + public static Service.SearchRequest.Body.Filter toGrpcMessage(ObjectFilter filter) { var objectMatchType = Types.MatchType.forNumber(filter.getMatchType().value); if (isNull(objectMatchType)) { diff --git a/models/src/main/java/info/FrostFS/sdk/mappers/object/ObjectFrostFSMapper.java b/models/src/main/java/info/FrostFS/sdk/mappers/object/ObjectFrostFSMapper.java new file mode 100644 index 0000000..22cc305 --- /dev/null +++ b/models/src/main/java/info/FrostFS/sdk/mappers/object/ObjectFrostFSMapper.java @@ -0,0 +1,16 @@ +package info.FrostFS.sdk.mappers.object; + +import frostFS.object.Types; +import info.FrostFS.sdk.dto.object.ObjectFrostFS; +import info.FrostFS.sdk.dto.object.ObjectId; + +public class ObjectFrostFSMapper { + + public static ObjectFrostFS toModel(Types.Object obj) { + return new ObjectFrostFS( + ObjectHeaderMapper.toModel(obj.getHeader()), + ObjectId.fromHash(obj.getObjectId().getValue().toByteArray()), + obj.getPayload().toByteArray() + ); + } +} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/ObjectHeaderMapper.java b/models/src/main/java/info/FrostFS/sdk/mappers/object/ObjectHeaderMapper.java similarity index 75% rename from modelsV2/src/main/java/info/FrostFS/sdk/mappers/ObjectHeaderMapper.java rename to models/src/main/java/info/FrostFS/sdk/mappers/object/ObjectHeaderMapper.java index ba38b14..d47dc55 100644 --- a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/ObjectHeaderMapper.java +++ b/models/src/main/java/info/FrostFS/sdk/mappers/object/ObjectHeaderMapper.java @@ -1,17 +1,21 @@ -package info.FrostFS.sdk.mappers; +package info.FrostFS.sdk.mappers.object; import frostFS.object.Types; +import info.FrostFS.sdk.dto.container.ContainerId; +import info.FrostFS.sdk.dto.object.ObjectAttribute; +import info.FrostFS.sdk.dto.object.ObjectHeader; import info.FrostFS.sdk.enums.ObjectType; -import info.FrostFS.sdk.jdo.ContainerId; -import info.FrostFS.sdk.jdo.ObjectAttribute; -import info.FrostFS.sdk.jdo.ObjectHeader; -import org.apache.commons.lang3.EnumUtils; +import info.FrostFS.sdk.mappers.VersionMapper; +import info.FrostFS.sdk.mappers.container.ContainerIdMapper; + +import java.util.stream.Collectors; import static java.util.Objects.isNull; public class ObjectHeaderMapper { + public static Types.Header toGrpcMessage(ObjectHeader header) { - var objectType = EnumUtils.getEnum(Types.ObjectType.class, header.getObjectType().name()); + var objectType = Types.ObjectType.forNumber(header.getObjectType().value); if (isNull(objectType)) { throw new IllegalArgumentException( String.format("Unknown ObjectType. Value: %s.", header.getObjectType().name()) @@ -40,7 +44,7 @@ public class ObjectHeaderMapper { return new ObjectHeader( ContainerId.fromHash(header.getContainerId().getValue().toByteArray()), objectType, - header.getAttributesList().stream().map(ObjectAttributeMapper::toModel).toArray(ObjectAttribute[]::new), + header.getAttributesList().stream().map(ObjectAttributeMapper::toModel).collect(Collectors.toList()), header.getPayloadLength(), VersionMapper.toModel(header.getVersion()) ); diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/ObjectIdMapper.java b/models/src/main/java/info/FrostFS/sdk/mappers/object/ObjectIdMapper.java similarity index 57% rename from modelsV2/src/main/java/info/FrostFS/sdk/mappers/ObjectIdMapper.java rename to models/src/main/java/info/FrostFS/sdk/mappers/object/ObjectIdMapper.java index 42200e2..437b4bb 100644 --- a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/ObjectIdMapper.java +++ b/models/src/main/java/info/FrostFS/sdk/mappers/object/ObjectIdMapper.java @@ -1,13 +1,18 @@ -package info.FrostFS.sdk.mappers; +package info.FrostFS.sdk.mappers.object; import com.google.protobuf.ByteString; import frostFS.refs.Types; -import info.FrostFS.sdk.jdo.ObjectId; +import info.FrostFS.sdk.dto.object.ObjectId; public class ObjectIdMapper { + public static Types.ObjectID toGrpcMessage(ObjectId objectId) { return Types.ObjectID.newBuilder() .setValue(ByteString.copyFrom(objectId.toHash())) .build(); } + + public static ObjectId toModel(Types.ObjectID objectId) { + return ObjectId.fromHash(objectId.getValue().toByteArray()); + } } diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/ObjectFrostFs.java b/modelsV2/src/main/java/info/FrostFS/sdk/jdo/ObjectFrostFs.java deleted file mode 100644 index c7cc689..0000000 --- a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/ObjectFrostFs.java +++ /dev/null @@ -1,37 +0,0 @@ -package info.FrostFS.sdk.jdo; - -public class ObjectFrostFs { - public ObjectHeader header; - public ObjectId objectId; - public byte[] payload; - - public ObjectFrostFs(ObjectHeader header, ObjectId objectId, byte[] payload) { - this.header = header; - this.objectId = objectId; - this.payload = payload; - } - - public ObjectHeader getHeader() { - return header; - } - - public void setHeader(ObjectHeader header) { - this.header = header; - } - - public ObjectId getObjectId() { - return objectId; - } - - public void setObjectId(ObjectId objectId) { - this.objectId = objectId; - } - - public byte[] getPayload() { - return payload; - } - - public void setPayload(byte[] payload) { - this.payload = payload; - } -} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/ObjectHeader.java b/modelsV2/src/main/java/info/FrostFS/sdk/jdo/ObjectHeader.java deleted file mode 100644 index 7dc795c..0000000 --- a/modelsV2/src/main/java/info/FrostFS/sdk/jdo/ObjectHeader.java +++ /dev/null @@ -1,71 +0,0 @@ -package info.FrostFS.sdk.jdo; - -import info.FrostFS.sdk.enums.ObjectType; - -public class ObjectHeader { - public ObjectAttribute[] attributes; - public ContainerId containerId; - public long size; - public ObjectType objectType; - public Version version; - - public ObjectHeader(ContainerId containerId, ObjectType objectType, ObjectAttribute[] attributes, long size, Version version) { - this.attributes = attributes; - this.containerId = containerId; - this.size = size; - this.objectType = objectType; - this.version = version; - } - - public ObjectHeader(ContainerId containerId, ObjectType type, ObjectAttribute[] attributes) { - this.attributes = attributes; - this.containerId = containerId; - this.objectType = type; - } - - public ObjectHeader(ContainerId containerId, ObjectAttribute[] attributes) { - this.attributes = attributes; - this.containerId = containerId; - this.objectType = ObjectType.REGULAR; - } - - public ObjectAttribute[] getAttributes() { - return attributes; - } - - public void setAttributes(ObjectAttribute[] attributes) { - this.attributes = attributes; - } - - public ContainerId getContainerId() { - return containerId; - } - - public void setContainerId(ContainerId containerId) { - this.containerId = containerId; - } - - public long getSize() { - return size; - } - - public void setSize(long size) { - this.size = size; - } - - public ObjectType getObjectType() { - return objectType; - } - - public void setObjectType(ObjectType objectType) { - this.objectType = objectType; - } - - public Version getVersion() { - return version; - } - - public void setVersion(Version version) { - this.version = version; - } -} diff --git a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/ObjectMapper.java b/modelsV2/src/main/java/info/FrostFS/sdk/mappers/ObjectMapper.java deleted file mode 100644 index b970456..0000000 --- a/modelsV2/src/main/java/info/FrostFS/sdk/mappers/ObjectMapper.java +++ /dev/null @@ -1,15 +0,0 @@ -package info.FrostFS.sdk.mappers; - -import frostFS.object.Types; -import info.FrostFS.sdk.jdo.ObjectFrostFs; -import info.FrostFS.sdk.jdo.ObjectId; - -public class ObjectMapper { - public static ObjectFrostFs toModel(Types.Object obj) { - return new ObjectFrostFs( - ObjectHeaderMapper.toModel(obj.getHeader()), - ObjectId.fromHash(obj.getObjectId().getValue().toByteArray()), - obj.getPayload().toByteArray() - ); - } -} diff --git a/pom.xml b/pom.xml index 5c16e71..8423cfb 100644 --- a/pom.xml +++ b/pom.xml @@ -9,10 +9,10 @@ 0.1.0 pom - protosV2 client cryptography - modelsV2 + models + protos diff --git a/protosV2/pom.xml b/protos/pom.xml similarity index 98% rename from protosV2/pom.xml rename to protos/pom.xml index d7d8630..cee5fb0 100644 --- a/protosV2/pom.xml +++ b/protos/pom.xml @@ -9,13 +9,13 @@ 0.1.0 - protosV2 + protos 11 11 3.23.0 - 1.62.2 + 1.65.1 UTF-8 diff --git a/protosV2/src/main/proto/accounting/service.proto b/protos/src/main/proto/accounting/service.proto similarity index 100% rename from protosV2/src/main/proto/accounting/service.proto rename to protos/src/main/proto/accounting/service.proto diff --git a/protosV2/src/main/proto/accounting/types.proto b/protos/src/main/proto/accounting/types.proto similarity index 100% rename from protosV2/src/main/proto/accounting/types.proto rename to protos/src/main/proto/accounting/types.proto diff --git a/protosV2/src/main/proto/acl/types.proto b/protos/src/main/proto/acl/types.proto similarity index 100% rename from protosV2/src/main/proto/acl/types.proto rename to protos/src/main/proto/acl/types.proto diff --git a/protosV2/src/main/proto/apemanager/service.proto b/protos/src/main/proto/apemanager/service.proto similarity index 100% rename from protosV2/src/main/proto/apemanager/service.proto rename to protos/src/main/proto/apemanager/service.proto diff --git a/protosV2/src/main/proto/apemanager/types.proto b/protos/src/main/proto/apemanager/types.proto similarity index 100% rename from protosV2/src/main/proto/apemanager/types.proto rename to protos/src/main/proto/apemanager/types.proto diff --git a/protosV2/src/main/proto/container/service.proto b/protos/src/main/proto/container/service.proto similarity index 100% rename from protosV2/src/main/proto/container/service.proto rename to protos/src/main/proto/container/service.proto diff --git a/protosV2/src/main/proto/container/types.proto b/protos/src/main/proto/container/types.proto similarity index 100% rename from protosV2/src/main/proto/container/types.proto rename to protos/src/main/proto/container/types.proto diff --git a/protosV2/src/main/proto/lock/types.proto b/protos/src/main/proto/lock/types.proto similarity index 100% rename from protosV2/src/main/proto/lock/types.proto rename to protos/src/main/proto/lock/types.proto diff --git a/protosV2/src/main/proto/netmap/service.proto b/protos/src/main/proto/netmap/service.proto similarity index 100% rename from protosV2/src/main/proto/netmap/service.proto rename to protos/src/main/proto/netmap/service.proto diff --git a/protosV2/src/main/proto/netmap/types.proto b/protos/src/main/proto/netmap/types.proto similarity index 100% rename from protosV2/src/main/proto/netmap/types.proto rename to protos/src/main/proto/netmap/types.proto diff --git a/protosV2/src/main/proto/object/service.proto b/protos/src/main/proto/object/service.proto similarity index 100% rename from protosV2/src/main/proto/object/service.proto rename to protos/src/main/proto/object/service.proto diff --git a/protosV2/src/main/proto/object/types.proto b/protos/src/main/proto/object/types.proto similarity index 100% rename from protosV2/src/main/proto/object/types.proto rename to protos/src/main/proto/object/types.proto diff --git a/protosV2/src/main/proto/refs/types.proto b/protos/src/main/proto/refs/types.proto similarity index 100% rename from protosV2/src/main/proto/refs/types.proto rename to protos/src/main/proto/refs/types.proto diff --git a/protosV2/src/main/proto/session/service.proto b/protos/src/main/proto/session/service.proto similarity index 100% rename from protosV2/src/main/proto/session/service.proto rename to protos/src/main/proto/session/service.proto diff --git a/protosV2/src/main/proto/session/types.proto b/protos/src/main/proto/session/types.proto similarity index 100% rename from protosV2/src/main/proto/session/types.proto rename to protos/src/main/proto/session/types.proto diff --git a/protosV2/src/main/proto/status/types.proto b/protos/src/main/proto/status/types.proto similarity index 100% rename from protosV2/src/main/proto/status/types.proto rename to protos/src/main/proto/status/types.proto diff --git a/protosV2/src/main/proto/tombstone/types.proto b/protos/src/main/proto/tombstone/types.proto similarity index 100% rename from protosV2/src/main/proto/tombstone/types.proto rename to protos/src/main/proto/tombstone/types.proto From a7fab6f31470f6166734021df2246d4265e1b98b Mon Sep 17 00:00:00 2001 From: Bruk Ori Date: Tue, 23 Jul 2024 19:30:07 +0300 Subject: [PATCH 06/38] [#1] code cleanup update README.md Signed-off-by: Ori Bruk --- README.md | 18 ++--- .../src/main/java/info/FrostFS/sdk/Main.java | 67 ------------------- .../FrostFS/sdk/constants/CryptoConst.java | 5 ++ .../sdk/{tools => jdo}/ClientEnvironment.java | 2 +- .../sdk/{tools => jdo}/ClientSettings.java | 2 +- .../FrostFS/sdk/{tools => jdo}/ECDsa.java | 2 +- .../sdk/{tools => jdo}/NetworkSettings.java | 2 +- .../{tools => jdo}/PutObjectParameters.java | 2 +- .../FrostFS/sdk/services/ContextAccessor.java | 2 +- .../FrostFS/sdk/services/FrostFSClient.java | 25 ++++--- .../FrostFS/sdk/services/NetmapClient.java | 2 +- .../FrostFS/sdk/services/ObjectClient.java | 2 +- .../sdk/services/impl/ContainerService.java | 14 ++-- .../sdk/services/impl/NetmapService.java | 10 +-- .../sdk/services/impl/ObjectReader.java | 11 +-- .../sdk/services/impl/ObjectService.java | 23 ++++--- .../sdk/services/impl/ObjectTools.java | 8 +-- .../sdk/services/impl/SearchReader.java | 2 +- .../sdk/services/impl/SessionService.java | 6 +- .../FrostFS/sdk/{ => tools}/GrpcClient.java | 6 +- .../info/FrostFS/sdk/tools/MessageHelper.java | 15 +++++ .../sdk/{ => tools}/RequestConstructor.java | 25 +++---- .../sdk/{ => tools}/RequestSigner.java | 58 ++++++++-------- .../FrostFS/sdk/{ => tools}/Verifier.java | 48 ++++++------- cryptography/pom.xml | 4 +- .../main/java/info/FrostFS/sdk/Helper.java | 4 +- models/pom.xml | 4 +- .../FrostFS/sdk/constants/FieldConst.java | 11 +++ 28 files changed, 177 insertions(+), 203 deletions(-) delete mode 100644 client/src/main/java/info/FrostFS/sdk/Main.java create mode 100644 client/src/main/java/info/FrostFS/sdk/constants/CryptoConst.java rename client/src/main/java/info/FrostFS/sdk/{tools => jdo}/ClientEnvironment.java (97%) rename client/src/main/java/info/FrostFS/sdk/{tools => jdo}/ClientSettings.java (97%) rename client/src/main/java/info/FrostFS/sdk/{tools => jdo}/ECDsa.java (95%) rename client/src/main/java/info/FrostFS/sdk/{tools => jdo}/NetworkSettings.java (99%) rename client/src/main/java/info/FrostFS/sdk/{tools => jdo}/PutObjectParameters.java (98%) rename client/src/main/java/info/FrostFS/sdk/{ => tools}/GrpcClient.java (73%) create mode 100644 client/src/main/java/info/FrostFS/sdk/tools/MessageHelper.java rename client/src/main/java/info/FrostFS/sdk/{ => tools}/RequestConstructor.java (61%) rename client/src/main/java/info/FrostFS/sdk/{ => tools}/RequestSigner.java (69%) rename client/src/main/java/info/FrostFS/sdk/{ => tools}/Verifier.java (70%) create mode 100644 models/src/main/java/info/FrostFS/sdk/constants/FieldConst.java diff --git a/README.md b/README.md index 94031a8..fec8945 100644 --- a/README.md +++ b/README.md @@ -21,18 +21,18 @@ neo-go wallet export -w -d ### Container ```java -import info.FrostFS.sdk.enums.BasicAcl; import info.FrostFS.sdk.dto.container.Container; import info.FrostFS.sdk.dto.netmap.PlacementPolicy; import info.FrostFS.sdk.dto.netmap.Replica; +import info.FrostFS.sdk.enums.BasicAcl; +import info.FrostFS.sdk.jdo.ClientSettings; import info.FrostFS.sdk.services.FrostFSClient; public class ContainerExample { public void example() { - Client client = new Client( < your_key >); - GrpcClient grpcClient = new GrpcClient( < your_host >); - FrostFSClient frostFSClient = new FrostFSClient(grpcClient, client); + ClientSettings clientSettings = new ClientSettings(, ); + FrostFSClient frostFSClient = new FrostFSClient(clientSettings); // Create container var placementPolicy = new PlacementPolicy(true, new Replica[]{new Replica(1)}); @@ -58,6 +58,7 @@ import info.FrostFS.sdk.dto.container.ContainerId; import info.FrostFS.sdk.dto.object.ObjectAttribute; import info.FrostFS.sdk.dto.object.ObjectFilter; import info.FrostFS.sdk.dto.object.ObjectHeader; +import info.FrostFS.sdk.jdo.PutObjectParameters; import info.FrostFS.sdk.services.FrostFSClient; import java.io.FileInputStream; @@ -66,9 +67,8 @@ import java.io.IOException; public class ObjectExample { public void example() { - Client client = new Client( < your_key >); - GrpcClient grpcClient = new GrpcClient( < your_host >); - FrostFSClient frostFSClient = new FrostFSClient(grpcClient, client); + ClientSettings clientSettings = new ClientSettings(, ); + FrostFSClient frostFSClient = new FrostFSClient(clientSettings); // Put object info.FrostFS.sdk.dto.object.ObjectId objectId; @@ -76,7 +76,9 @@ public class ObjectExample { var cat = new ObjectHeader( containerId, ObjectType.REGULAR, new ObjectAttribute[]{new ObjectAttribute("Filename", "cat.jpg")} ); - objectId = frostFSClient.putObject(cat, fis); + + var params = new PutObjectParameters(cat, fis); + objectId = frostFSClient.putObject(params); } catch (IOException e) { throw new RuntimeException(e); } diff --git a/client/src/main/java/info/FrostFS/sdk/Main.java b/client/src/main/java/info/FrostFS/sdk/Main.java deleted file mode 100644 index 4c854fe..0000000 --- a/client/src/main/java/info/FrostFS/sdk/Main.java +++ /dev/null @@ -1,67 +0,0 @@ -package info.FrostFS.sdk; - -import info.FrostFS.sdk.dto.container.Container; -import info.FrostFS.sdk.dto.container.ContainerId; -import info.FrostFS.sdk.enums.BasicAcl; -import info.FrostFS.sdk.enums.ObjectType; -import info.FrostFS.sdk.dto.netmap.PlacementPolicy; -import info.FrostFS.sdk.dto.netmap.Replica; -import info.FrostFS.sdk.dto.object.ObjectAttribute; -import info.FrostFS.sdk.dto.object.ObjectFrostFS; -import info.FrostFS.sdk.dto.object.ObjectHeader; -import info.FrostFS.sdk.dto.object.ObjectId; -import info.FrostFS.sdk.services.FrostFSClient; -import info.FrostFS.sdk.tools.ClientSettings; -import info.FrostFS.sdk.tools.PutObjectParameters; - -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.util.Collections; -import java.util.List; - -public class Main { - public static void main(String[] args) throws Exception { - ClientSettings clientSettings = new ClientSettings("KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq", "http://localhost:8080"); - FrostFSClient frostFSClient = new FrostFSClient(clientSettings); -/* - var res2 = frostFSClient.searchObjectsAsync(new ContainerId("EQGx2QeYHJb53uRwYGzcQaW191sZpdNrjutk6veUSV2R")); -*/ - var placementPolicy = new PlacementPolicy(true, new Replica[]{new Replica(1)}); - var containerId = frostFSClient.createContainer(new Container(BasicAcl.PUBLIC_RW, placementPolicy)); - Thread.sleep(1000); - - FileInputStream file = null; - try { - file = new FileInputStream("/home/ori/Desktop/cat.jpg"); - var cat = new ObjectHeader(containerId, ObjectType.REGULAR, Collections.singletonList(new ObjectAttribute("Filename", "cat3.jpg"))); - - var params = new PutObjectParameters(cat, file, false, 1024); - var tty = frostFSClient.putObject(params); - - System.out.println(1); - } catch (FileNotFoundException e) { - throw new RuntimeException(e); - } - - - var res = frostFSClient.searchObjects(containerId); - - ObjectFrostFS res3 = frostFSClient - .getObject(new ContainerId("EQGx2QeYHJb53uRwYGzcQaW191sZpdNrjutk6veUSV2R"), new ObjectId("85orCLKSu3X1jGiTFmwmTUsBU88RBARNwuRwrEy5pyww")); - - - var ttttt = frostFSClient.getNetworkSettings(); - Container container2 = frostFSClient.getContainer(new ContainerId("EQGx2QeYHJb53uRwYGzcQaW191sZpdNrjutk6veUSV2R")); - - - - - Container container = frostFSClient.getContainer(containerId); - List containerIds = frostFSClient.listContainers(); -/* - frostFSClient.deleteContainerAsync(containerId); -*/ - containerIds = frostFSClient.listContainers(); - System.out.println(); - } -} \ No newline at end of file diff --git a/client/src/main/java/info/FrostFS/sdk/constants/CryptoConst.java b/client/src/main/java/info/FrostFS/sdk/constants/CryptoConst.java new file mode 100644 index 0000000..742ef60 --- /dev/null +++ b/client/src/main/java/info/FrostFS/sdk/constants/CryptoConst.java @@ -0,0 +1,5 @@ +package info.FrostFS.sdk.constants; + +public class CryptoConst { + public static final String SIGNATURE_ALGORITHM = "NONEwithECDSAinP1363Format"; +} diff --git a/client/src/main/java/info/FrostFS/sdk/tools/ClientEnvironment.java b/client/src/main/java/info/FrostFS/sdk/jdo/ClientEnvironment.java similarity index 97% rename from client/src/main/java/info/FrostFS/sdk/tools/ClientEnvironment.java rename to client/src/main/java/info/FrostFS/sdk/jdo/ClientEnvironment.java index ee1d8d4..f770057 100644 --- a/client/src/main/java/info/FrostFS/sdk/tools/ClientEnvironment.java +++ b/client/src/main/java/info/FrostFS/sdk/jdo/ClientEnvironment.java @@ -1,4 +1,4 @@ -package info.FrostFS.sdk.tools; +package info.FrostFS.sdk.jdo; import info.FrostFS.sdk.dto.OwnerId; import info.FrostFS.sdk.dto.Version; diff --git a/client/src/main/java/info/FrostFS/sdk/tools/ClientSettings.java b/client/src/main/java/info/FrostFS/sdk/jdo/ClientSettings.java similarity index 97% rename from client/src/main/java/info/FrostFS/sdk/tools/ClientSettings.java rename to client/src/main/java/info/FrostFS/sdk/jdo/ClientSettings.java index 2b25083..c7f5de0 100644 --- a/client/src/main/java/info/FrostFS/sdk/tools/ClientSettings.java +++ b/client/src/main/java/info/FrostFS/sdk/jdo/ClientSettings.java @@ -1,4 +1,4 @@ -package info.FrostFS.sdk.tools; +package info.FrostFS.sdk.jdo; import io.grpc.ChannelCredentials; import org.apache.commons.lang3.StringUtils; diff --git a/client/src/main/java/info/FrostFS/sdk/tools/ECDsa.java b/client/src/main/java/info/FrostFS/sdk/jdo/ECDsa.java similarity index 95% rename from client/src/main/java/info/FrostFS/sdk/tools/ECDsa.java rename to client/src/main/java/info/FrostFS/sdk/jdo/ECDsa.java index 98ccfc0..e3afee7 100644 --- a/client/src/main/java/info/FrostFS/sdk/tools/ECDsa.java +++ b/client/src/main/java/info/FrostFS/sdk/jdo/ECDsa.java @@ -1,4 +1,4 @@ -package info.FrostFS.sdk.tools; +package info.FrostFS.sdk.jdo; import java.security.PrivateKey; diff --git a/client/src/main/java/info/FrostFS/sdk/tools/NetworkSettings.java b/client/src/main/java/info/FrostFS/sdk/jdo/NetworkSettings.java similarity index 99% rename from client/src/main/java/info/FrostFS/sdk/tools/NetworkSettings.java rename to client/src/main/java/info/FrostFS/sdk/jdo/NetworkSettings.java index d6971b3..680d3d3 100644 --- a/client/src/main/java/info/FrostFS/sdk/tools/NetworkSettings.java +++ b/client/src/main/java/info/FrostFS/sdk/jdo/NetworkSettings.java @@ -1,4 +1,4 @@ -package info.FrostFS.sdk.tools; +package info.FrostFS.sdk.jdo; import java.util.HashMap; import java.util.Map; diff --git a/client/src/main/java/info/FrostFS/sdk/tools/PutObjectParameters.java b/client/src/main/java/info/FrostFS/sdk/jdo/PutObjectParameters.java similarity index 98% rename from client/src/main/java/info/FrostFS/sdk/tools/PutObjectParameters.java rename to client/src/main/java/info/FrostFS/sdk/jdo/PutObjectParameters.java index ca62737..51082a3 100644 --- a/client/src/main/java/info/FrostFS/sdk/tools/PutObjectParameters.java +++ b/client/src/main/java/info/FrostFS/sdk/jdo/PutObjectParameters.java @@ -1,4 +1,4 @@ -package info.FrostFS.sdk.tools; +package info.FrostFS.sdk.jdo; import info.FrostFS.sdk.dto.object.ObjectHeader; diff --git a/client/src/main/java/info/FrostFS/sdk/services/ContextAccessor.java b/client/src/main/java/info/FrostFS/sdk/services/ContextAccessor.java index d3880a9..58f9c43 100644 --- a/client/src/main/java/info/FrostFS/sdk/services/ContextAccessor.java +++ b/client/src/main/java/info/FrostFS/sdk/services/ContextAccessor.java @@ -1,6 +1,6 @@ package info.FrostFS.sdk.services; -import info.FrostFS.sdk.tools.ClientEnvironment; +import info.FrostFS.sdk.jdo.ClientEnvironment; public class ContextAccessor { private final ClientEnvironment context; diff --git a/client/src/main/java/info/FrostFS/sdk/services/FrostFSClient.java b/client/src/main/java/info/FrostFS/sdk/services/FrostFSClient.java index 64ba93c..b0c7f1b 100644 --- a/client/src/main/java/info/FrostFS/sdk/services/FrostFSClient.java +++ b/client/src/main/java/info/FrostFS/sdk/services/FrostFSClient.java @@ -1,29 +1,32 @@ package info.FrostFS.sdk.services; import frostFS.session.Types; -import info.FrostFS.sdk.tools.ClientSettings; -import info.FrostFS.sdk.tools.PutObjectParameters; -import info.FrostFS.sdk.dto.container.Container; -import info.FrostFS.sdk.dto.container.ContainerId; import info.FrostFS.sdk.dto.SessionToken; import info.FrostFS.sdk.dto.Version; +import info.FrostFS.sdk.dto.container.Container; +import info.FrostFS.sdk.dto.container.ContainerId; import info.FrostFS.sdk.dto.netmap.NetmapSnapshot; import info.FrostFS.sdk.dto.netmap.NodeInfo; import info.FrostFS.sdk.dto.object.ObjectFilter; import info.FrostFS.sdk.dto.object.ObjectFrostFS; import info.FrostFS.sdk.dto.object.ObjectHeader; import info.FrostFS.sdk.dto.object.ObjectId; +import info.FrostFS.sdk.jdo.ClientEnvironment; +import info.FrostFS.sdk.jdo.ClientSettings; +import info.FrostFS.sdk.jdo.NetworkSettings; +import info.FrostFS.sdk.jdo.PutObjectParameters; import info.FrostFS.sdk.services.impl.*; -import info.FrostFS.sdk.tools.ClientEnvironment; -import info.FrostFS.sdk.tools.NetworkSettings; import io.grpc.Channel; import java.util.List; -import static info.FrostFS.sdk.GrpcClient.initGrpcChannel; +import static info.FrostFS.sdk.tools.GrpcClient.initGrpcChannel; import static java.util.Objects.isNull; public class FrostFSClient implements ContainerClient, ObjectClient, NetmapClient, SessionClient, ToolsClient { + private static final String ERROR_CLIENT_OPTIONS_INIT = "Options must be initialized."; + private static final String ERROR_VERSION_SUPPORT_TEMPLATE = "FrostFS %s is not supported."; + private final ContainerService containerService; private final NetmapService netmapService; private final ObjectService objectService; @@ -32,7 +35,7 @@ public class FrostFSClient implements ContainerClient, ObjectClient, NetmapClien public FrostFSClient(ClientSettings clientSettings) { if (isNull(clientSettings)) { - throw new IllegalArgumentException("Options must be initialized"); + throw new IllegalArgumentException(ERROR_CLIENT_OPTIONS_INIT); } clientSettings.validate(); @@ -53,9 +56,9 @@ public class FrostFSClient implements ContainerClient, ObjectClient, NetmapClien private void checkFrostFsVersionSupport(Version version) { var localNodeInfo = netmapService.getLocalNodeInfo(); if (!localNodeInfo.getVersion().isSupported(version)) { - var msg = String.format("FrostFS %s is not supported.", localNodeInfo.getVersion()); - System.out.println(msg); - throw new IllegalArgumentException(msg); + throw new IllegalArgumentException( + String.format(ERROR_VERSION_SUPPORT_TEMPLATE, localNodeInfo.getVersion()) + ); } } diff --git a/client/src/main/java/info/FrostFS/sdk/services/NetmapClient.java b/client/src/main/java/info/FrostFS/sdk/services/NetmapClient.java index 80bbf1e..f053dfa 100644 --- a/client/src/main/java/info/FrostFS/sdk/services/NetmapClient.java +++ b/client/src/main/java/info/FrostFS/sdk/services/NetmapClient.java @@ -2,7 +2,7 @@ package info.FrostFS.sdk.services; import info.FrostFS.sdk.dto.netmap.NetmapSnapshot; import info.FrostFS.sdk.dto.netmap.NodeInfo; -import info.FrostFS.sdk.tools.NetworkSettings; +import info.FrostFS.sdk.jdo.NetworkSettings; public interface NetmapClient { NetmapSnapshot getNetmapSnapshot(); diff --git a/client/src/main/java/info/FrostFS/sdk/services/ObjectClient.java b/client/src/main/java/info/FrostFS/sdk/services/ObjectClient.java index f19fb61..96ae2a3 100644 --- a/client/src/main/java/info/FrostFS/sdk/services/ObjectClient.java +++ b/client/src/main/java/info/FrostFS/sdk/services/ObjectClient.java @@ -1,11 +1,11 @@ package info.FrostFS.sdk.services; import info.FrostFS.sdk.dto.container.ContainerId; -import info.FrostFS.sdk.tools.PutObjectParameters; import info.FrostFS.sdk.dto.object.ObjectFilter; import info.FrostFS.sdk.dto.object.ObjectFrostFS; import info.FrostFS.sdk.dto.object.ObjectHeader; import info.FrostFS.sdk.dto.object.ObjectId; +import info.FrostFS.sdk.jdo.PutObjectParameters; public interface ObjectClient { ObjectHeader getObjectHead(ContainerId containerId, ObjectId objectId); diff --git a/client/src/main/java/info/FrostFS/sdk/services/impl/ContainerService.java b/client/src/main/java/info/FrostFS/sdk/services/impl/ContainerService.java index 4383579..a1df64c 100644 --- a/client/src/main/java/info/FrostFS/sdk/services/impl/ContainerService.java +++ b/client/src/main/java/info/FrostFS/sdk/services/impl/ContainerService.java @@ -2,23 +2,23 @@ package info.FrostFS.sdk.services.impl; import frostFS.container.ContainerServiceGrpc; import frostFS.container.Service; -import info.FrostFS.sdk.Verifier; import info.FrostFS.sdk.dto.container.Container; import info.FrostFS.sdk.dto.container.ContainerId; -import info.FrostFS.sdk.mappers.container.ContainerIdMapper; -import info.FrostFS.sdk.mappers.container.ContainerMapper; +import info.FrostFS.sdk.jdo.ClientEnvironment; import info.FrostFS.sdk.mappers.OwnerIdMapper; import info.FrostFS.sdk.mappers.VersionMapper; +import info.FrostFS.sdk.mappers.container.ContainerIdMapper; +import info.FrostFS.sdk.mappers.container.ContainerMapper; import info.FrostFS.sdk.services.ContainerClient; import info.FrostFS.sdk.services.ContextAccessor; -import info.FrostFS.sdk.tools.ClientEnvironment; +import info.FrostFS.sdk.tools.Verifier; import java.util.List; import java.util.stream.Collectors; -import static info.FrostFS.sdk.RequestConstructor.addMetaHeader; -import static info.FrostFS.sdk.RequestSigner.sign; -import static info.FrostFS.sdk.RequestSigner.signRFC6979; +import static info.FrostFS.sdk.tools.RequestConstructor.addMetaHeader; +import static info.FrostFS.sdk.tools.RequestSigner.sign; +import static info.FrostFS.sdk.tools.RequestSigner.signRFC6979; public class ContainerService extends ContextAccessor implements ContainerClient { private final ContainerServiceGrpc.ContainerServiceBlockingStub serviceBlockingStub; diff --git a/client/src/main/java/info/FrostFS/sdk/services/impl/NetmapService.java b/client/src/main/java/info/FrostFS/sdk/services/impl/NetmapService.java index e55c655..135e95b 100644 --- a/client/src/main/java/info/FrostFS/sdk/services/impl/NetmapService.java +++ b/client/src/main/java/info/FrostFS/sdk/services/impl/NetmapService.java @@ -3,20 +3,20 @@ package info.FrostFS.sdk.services.impl; import frostFS.netmap.NetmapServiceGrpc; import frostFS.netmap.Service; import frostFS.netmap.Types; -import info.FrostFS.sdk.Verifier; import info.FrostFS.sdk.dto.netmap.NetmapSnapshot; import info.FrostFS.sdk.dto.netmap.NodeInfo; +import info.FrostFS.sdk.jdo.ClientEnvironment; +import info.FrostFS.sdk.jdo.NetworkSettings; import info.FrostFS.sdk.mappers.netmap.NetmapSnapshotMapper; import info.FrostFS.sdk.mappers.netmap.NodeInfoMapper; import info.FrostFS.sdk.services.ContextAccessor; import info.FrostFS.sdk.services.NetmapClient; -import info.FrostFS.sdk.tools.ClientEnvironment; -import info.FrostFS.sdk.tools.NetworkSettings; +import info.FrostFS.sdk.tools.Verifier; import java.nio.charset.StandardCharsets; -import static info.FrostFS.sdk.RequestConstructor.addMetaHeader; -import static info.FrostFS.sdk.RequestSigner.sign; +import static info.FrostFS.sdk.tools.RequestConstructor.addMetaHeader; +import static info.FrostFS.sdk.tools.RequestSigner.sign; import static java.util.Objects.nonNull; public class NetmapService extends ContextAccessor implements NetmapClient { diff --git a/client/src/main/java/info/FrostFS/sdk/services/impl/ObjectReader.java b/client/src/main/java/info/FrostFS/sdk/services/impl/ObjectReader.java index 8350bd7..116b3c2 100644 --- a/client/src/main/java/info/FrostFS/sdk/services/impl/ObjectReader.java +++ b/client/src/main/java/info/FrostFS/sdk/services/impl/ObjectReader.java @@ -2,11 +2,14 @@ package info.FrostFS.sdk.services.impl; import frostFS.object.Service; import frostFS.object.Types; -import info.FrostFS.sdk.Verifier; +import info.FrostFS.sdk.tools.Verifier; import java.util.Iterator; public class ObjectReader { + public static final String ERROR_UNEXPECTED_STREAM = "unexpected end of stream"; + public static final String ERROR_UNEXPECTED_MESSAGE_TYPE = "unexpected message type"; + public Iterator call; public ObjectReader(Iterator call) { @@ -15,14 +18,14 @@ public class ObjectReader { public Types.Object readHeader() { if (!call.hasNext()) { - throw new IllegalArgumentException("unexpected end of stream"); + throw new IllegalArgumentException(ERROR_UNEXPECTED_STREAM); } var response = call.next(); Verifier.checkResponse(response); if (response.getBody().getObjectPartCase().getNumber() != Service.GetResponse.Body.INIT_FIELD_NUMBER) { - throw new IllegalArgumentException("unexpected message type"); + throw new IllegalArgumentException(ERROR_UNEXPECTED_MESSAGE_TYPE); } return Types.Object.newBuilder() @@ -40,7 +43,7 @@ public class ObjectReader { Verifier.checkResponse(response); if (response.getBody().getObjectPartCase().getNumber() != Service.GetResponse.Body.CHUNK_FIELD_NUMBER) { - throw new IllegalArgumentException("unexpected message type"); + throw new IllegalArgumentException(ERROR_UNEXPECTED_MESSAGE_TYPE); } return response.getBody().getChunk().toByteArray(); diff --git a/client/src/main/java/info/FrostFS/sdk/services/impl/ObjectService.java b/client/src/main/java/info/FrostFS/sdk/services/impl/ObjectService.java index 4fbe78d..c9520e3 100644 --- a/client/src/main/java/info/FrostFS/sdk/services/impl/ObjectService.java +++ b/client/src/main/java/info/FrostFS/sdk/services/impl/ObjectService.java @@ -5,21 +5,22 @@ import com.google.protobuf.ByteString; import frostFS.object.ObjectServiceGrpc; import frostFS.object.Service; import frostFS.refs.Types; -import info.FrostFS.sdk.Verifier; import info.FrostFS.sdk.constants.AppConst; -import info.FrostFS.sdk.dto.container.ContainerId; import info.FrostFS.sdk.dto.Split; +import info.FrostFS.sdk.dto.container.ContainerId; import info.FrostFS.sdk.dto.object.*; -import info.FrostFS.sdk.mappers.*; +import info.FrostFS.sdk.jdo.ClientEnvironment; +import info.FrostFS.sdk.jdo.PutObjectParameters; +import info.FrostFS.sdk.mappers.OwnerIdMapper; +import info.FrostFS.sdk.mappers.VersionMapper; import info.FrostFS.sdk.mappers.container.ContainerIdMapper; import info.FrostFS.sdk.mappers.object.ObjectFilterMapper; +import info.FrostFS.sdk.mappers.object.ObjectFrostFSMapper; import info.FrostFS.sdk.mappers.object.ObjectHeaderMapper; import info.FrostFS.sdk.mappers.object.ObjectIdMapper; -import info.FrostFS.sdk.mappers.object.ObjectFrostFSMapper; import info.FrostFS.sdk.services.ContextAccessor; import info.FrostFS.sdk.services.ObjectClient; -import info.FrostFS.sdk.tools.ClientEnvironment; -import info.FrostFS.sdk.tools.PutObjectParameters; +import info.FrostFS.sdk.tools.Verifier; import org.apache.commons.collections4.CollectionUtils; import java.io.FileInputStream; @@ -29,12 +30,14 @@ import java.util.Arrays; import java.util.List; import static info.FrostFS.sdk.Helper.getSha256; -import static info.FrostFS.sdk.RequestConstructor.addMetaHeader; -import static info.FrostFS.sdk.RequestConstructor.addObjectSessionToken; -import static info.FrostFS.sdk.RequestSigner.sign; +import static info.FrostFS.sdk.tools.RequestConstructor.addMetaHeader; +import static info.FrostFS.sdk.tools.RequestConstructor.addObjectSessionToken; +import static info.FrostFS.sdk.tools.RequestSigner.sign; import static java.util.Objects.nonNull; public class ObjectService extends ContextAccessor implements ObjectClient { + private static final String ERROR_PAYLOAD = "PayloadLength must be specified"; + private final ObjectServiceGrpc.ObjectServiceBlockingStub objectServiceBlockingClient; private final ObjectServiceGrpc.ObjectServiceStub objectServiceClient; private final ObjectTools objectTools; @@ -257,7 +260,7 @@ public class ObjectService extends ContextAccessor implements ObjectClient { var objectSize = (int) Math.min(payloadSize, networkSettings.getMaxObjectSize()); var fullLength = header.getPayloadLength() == 0 ? payloadSize : header.getPayloadLength(); if (fullLength == 0) { - throw new IllegalArgumentException("Payload stream must be able to seek or PayloadLength must be specified"); + throw new IllegalArgumentException(ERROR_PAYLOAD); } var buffer = new byte[objectSize]; diff --git a/client/src/main/java/info/FrostFS/sdk/services/impl/ObjectTools.java b/client/src/main/java/info/FrostFS/sdk/services/impl/ObjectTools.java index 31fbebd..412fa66 100644 --- a/client/src/main/java/info/FrostFS/sdk/services/impl/ObjectTools.java +++ b/client/src/main/java/info/FrostFS/sdk/services/impl/ObjectTools.java @@ -5,17 +5,17 @@ import frostFS.object.Types; import info.FrostFS.sdk.dto.object.ObjectFrostFS; import info.FrostFS.sdk.dto.object.ObjectHeader; import info.FrostFS.sdk.dto.object.ObjectId; -import info.FrostFS.sdk.mappers.object.ObjectHeaderMapper; -import info.FrostFS.sdk.mappers.object.ObjectIdMapper; +import info.FrostFS.sdk.jdo.ClientEnvironment; import info.FrostFS.sdk.mappers.OwnerIdMapper; import info.FrostFS.sdk.mappers.VersionMapper; +import info.FrostFS.sdk.mappers.object.ObjectHeaderMapper; +import info.FrostFS.sdk.mappers.object.ObjectIdMapper; import info.FrostFS.sdk.services.ContextAccessor; import info.FrostFS.sdk.services.ToolsClient; -import info.FrostFS.sdk.tools.ClientEnvironment; import org.apache.commons.collections4.ListUtils; import static info.FrostFS.sdk.Helper.getSha256; -import static info.FrostFS.sdk.RequestSigner.signData; +import static info.FrostFS.sdk.tools.RequestSigner.signData; import static java.util.Objects.nonNull; public class ObjectTools extends ContextAccessor implements ToolsClient { diff --git a/client/src/main/java/info/FrostFS/sdk/services/impl/SearchReader.java b/client/src/main/java/info/FrostFS/sdk/services/impl/SearchReader.java index b0d3620..864d6e5 100644 --- a/client/src/main/java/info/FrostFS/sdk/services/impl/SearchReader.java +++ b/client/src/main/java/info/FrostFS/sdk/services/impl/SearchReader.java @@ -1,7 +1,7 @@ package info.FrostFS.sdk.services.impl; import frostFS.object.Service; -import info.FrostFS.sdk.Verifier; +import info.FrostFS.sdk.tools.Verifier; import java.util.Iterator; import java.util.List; diff --git a/client/src/main/java/info/FrostFS/sdk/services/impl/SessionService.java b/client/src/main/java/info/FrostFS/sdk/services/impl/SessionService.java index cae3cb6..35e5967 100644 --- a/client/src/main/java/info/FrostFS/sdk/services/impl/SessionService.java +++ b/client/src/main/java/info/FrostFS/sdk/services/impl/SessionService.java @@ -4,14 +4,14 @@ import frostFS.session.Service; import frostFS.session.SessionServiceGrpc; import frostFS.session.Types; import info.FrostFS.sdk.dto.SessionToken; +import info.FrostFS.sdk.jdo.ClientEnvironment; import info.FrostFS.sdk.mappers.OwnerIdMapper; import info.FrostFS.sdk.mappers.SessionMapper; import info.FrostFS.sdk.services.ContextAccessor; import info.FrostFS.sdk.services.SessionClient; -import info.FrostFS.sdk.tools.ClientEnvironment; -import static info.FrostFS.sdk.RequestConstructor.addMetaHeader; -import static info.FrostFS.sdk.RequestSigner.sign; +import static info.FrostFS.sdk.tools.RequestConstructor.addMetaHeader; +import static info.FrostFS.sdk.tools.RequestSigner.sign; public class SessionService extends ContextAccessor implements SessionClient { private final SessionServiceGrpc.SessionServiceBlockingStub serviceBlockingStub; diff --git a/client/src/main/java/info/FrostFS/sdk/GrpcClient.java b/client/src/main/java/info/FrostFS/sdk/tools/GrpcClient.java similarity index 73% rename from client/src/main/java/info/FrostFS/sdk/GrpcClient.java rename to client/src/main/java/info/FrostFS/sdk/tools/GrpcClient.java index 9062017..61cfee2 100644 --- a/client/src/main/java/info/FrostFS/sdk/GrpcClient.java +++ b/client/src/main/java/info/FrostFS/sdk/tools/GrpcClient.java @@ -1,4 +1,4 @@ -package info.FrostFS.sdk; +package info.FrostFS.sdk.tools; import io.grpc.Channel; import io.grpc.ChannelCredentials; @@ -10,6 +10,7 @@ import java.net.URISyntaxException; import static java.util.Objects.isNull; public class GrpcClient { + private static final String ERROR_INVALID_HOST_TEMPLATE = "Host %s has invalid format. Error: %s"; public static Channel initGrpcChannel(String host, ChannelCredentials creds) { try { @@ -19,8 +20,7 @@ public class GrpcClient { return channelBuilder.usePlaintext().build(); } catch (URISyntaxException exp) { - var message = String.format("Host %s has invalid format. Error: %s", host, exp.getMessage()); - throw new IllegalArgumentException(message); + throw new IllegalArgumentException(String.format(ERROR_INVALID_HOST_TEMPLATE, host, exp.getMessage())); } } } diff --git a/client/src/main/java/info/FrostFS/sdk/tools/MessageHelper.java b/client/src/main/java/info/FrostFS/sdk/tools/MessageHelper.java new file mode 100644 index 0000000..4e11b06 --- /dev/null +++ b/client/src/main/java/info/FrostFS/sdk/tools/MessageHelper.java @@ -0,0 +1,15 @@ +package info.FrostFS.sdk.tools; + +import com.google.protobuf.Message; +import com.google.protobuf.MessageOrBuilder; + +public class MessageHelper { + + public static Message getField(MessageOrBuilder messageOrBuilder, String fieldName) { + return (Message) messageOrBuilder.getField(messageOrBuilder.getDescriptorForType().findFieldByName(fieldName)); + } + + public static void setField(Message.Builder builder, String fieldName, Object value) { + builder.setField(builder.getDescriptorForType().findFieldByName(fieldName), value); + } +} diff --git a/client/src/main/java/info/FrostFS/sdk/RequestConstructor.java b/client/src/main/java/info/FrostFS/sdk/tools/RequestConstructor.java similarity index 61% rename from client/src/main/java/info/FrostFS/sdk/RequestConstructor.java rename to client/src/main/java/info/FrostFS/sdk/tools/RequestConstructor.java index 28daecc..3d8d02a 100644 --- a/client/src/main/java/info/FrostFS/sdk/RequestConstructor.java +++ b/client/src/main/java/info/FrostFS/sdk/tools/RequestConstructor.java @@ -1,36 +1,37 @@ -package info.FrostFS.sdk; +package info.FrostFS.sdk.tools; -import com.google.protobuf.AbstractMessage; +import com.google.protobuf.Message; import frostFS.session.Types; import info.FrostFS.sdk.dto.MetaHeader; +import info.FrostFS.sdk.jdo.ECDsa; import info.FrostFS.sdk.mappers.MetaHeaderMapper; -import info.FrostFS.sdk.tools.ECDsa; -import static info.FrostFS.sdk.RequestSigner.signMessagePart; +import static info.FrostFS.sdk.constants.FieldConst.META_HEADER_FIELD_NAME; +import static info.FrostFS.sdk.tools.MessageHelper.getField; +import static info.FrostFS.sdk.tools.MessageHelper.setField; +import static info.FrostFS.sdk.tools.RequestSigner.signMessagePart; import static java.util.Objects.isNull; public class RequestConstructor { - public static void addMetaHeader(AbstractMessage.Builder request) { + public static void addMetaHeader(Message.Builder request) { addMetaHeader(request, null); } - public static void addMetaHeader(AbstractMessage.Builder request, Types.RequestMetaHeader metaHeader) { + public static void addMetaHeader(Message.Builder request, Types.RequestMetaHeader metaHeader) { if (isNull(metaHeader) || metaHeader.getSerializedSize() == 0) { metaHeader = MetaHeaderMapper.toGrpcMessage(MetaHeader.getDefault()); - var field = request.getDescriptorForType().findFieldByName("meta_header"); - request.setField(field, metaHeader); + setField(request, META_HEADER_FIELD_NAME, metaHeader); } } - public static void addObjectSessionToken(AbstractMessage.Builder request, + public static void addObjectSessionToken(Message.Builder request, Types.SessionToken sessionToken, frostFS.refs.Types.ContainerID cid, frostFS.refs.Types.ObjectID oid, Types.ObjectSessionContext.Verb verb, ECDsa key) { - var headerField = request.getDescriptorForType().findFieldByName("meta_header"); - var header = (Types.RequestMetaHeader) request.getField(headerField); + var header = (Types.RequestMetaHeader) getField(request, META_HEADER_FIELD_NAME); if (header.getSessionToken().getSerializedSize() > 0) { return; } @@ -46,6 +47,6 @@ public class RequestConstructor { .setBody(body) .build(); - request.setField(headerField, header.toBuilder().setSessionToken(sessionToken).build()); + setField(request, META_HEADER_FIELD_NAME, header.toBuilder().setSessionToken(sessionToken).build()); } } diff --git a/client/src/main/java/info/FrostFS/sdk/RequestSigner.java b/client/src/main/java/info/FrostFS/sdk/tools/RequestSigner.java similarity index 69% rename from client/src/main/java/info/FrostFS/sdk/RequestSigner.java rename to client/src/main/java/info/FrostFS/sdk/tools/RequestSigner.java index a26dadf..daca2cc 100644 --- a/client/src/main/java/info/FrostFS/sdk/RequestSigner.java +++ b/client/src/main/java/info/FrostFS/sdk/tools/RequestSigner.java @@ -1,9 +1,9 @@ -package info.FrostFS.sdk; +package info.FrostFS.sdk.tools; -import com.google.protobuf.AbstractMessage; import com.google.protobuf.ByteString; +import com.google.protobuf.Message; import frostFS.session.Types; -import info.FrostFS.sdk.tools.ECDsa; +import info.FrostFS.sdk.jdo.ECDsa; import org.apache.commons.codec.digest.DigestUtils; import org.bouncycastle.asn1.sec.SECNamedCurves; import org.bouncycastle.asn1.sec.SECObjectIdentifiers; @@ -15,17 +15,22 @@ import org.bouncycastle.crypto.signers.HMacDSAKCalculator; import java.math.BigInteger; import java.security.Signature; +import static info.FrostFS.sdk.constants.CryptoConst.SIGNATURE_ALGORITHM; +import static info.FrostFS.sdk.constants.FieldConst.*; +import static info.FrostFS.sdk.tools.MessageHelper.getField; +import static info.FrostFS.sdk.tools.MessageHelper.setField; import static org.bouncycastle.crypto.util.DigestFactory.createSHA256; import static org.bouncycastle.util.BigIntegers.asUnsignedByteArray; public class RequestSigner { + public static final String ERROR_UNSUPPORTED_TYPE_TEMPLATE = "Unsupported message type: %s"; public static final int RFC6979_SIGNATURE_SIZE = 64; public static byte[] signData(ECDsa key, byte[] data) { var hash = new byte[65]; hash[0] = 0x04; try { - Signature signature = Signature.getInstance("NONEwithECDSAinP1363Format"); + Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); signature.initSign(key.getPrivateKey()); signature.update(DigestUtils.sha512(data)); byte[] sig = signature.sign(); @@ -62,7 +67,7 @@ public class RequestSigner { return signature; } - public static frostFS.refs.Types.SignatureRFC6979 signRFC6979(ECDsa key, AbstractMessage message) { + public static frostFS.refs.Types.SignatureRFC6979 signRFC6979(ECDsa key, Message message) { return frostFS.refs.Types.SignatureRFC6979.newBuilder() .setKey(ByteString.copyFrom(key.getPublicKeyByte())) .setSign(ByteString.copyFrom(signRFC6979(key, message.toByteArray()))) @@ -76,7 +81,7 @@ public class RequestSigner { .build(); } - public static frostFS.refs.Types.Signature signMessagePart(ECDsa key, AbstractMessage data) { + public static frostFS.refs.Types.Signature signMessagePart(ECDsa key, Message data) { var data2Sign = data.getSerializedSize() == 0 ? new byte[]{} : data.toByteArray(); return frostFS.refs.Types.Signature.newBuilder() .setKey(ByteString.copyFrom(key.getPublicKeyByte())) @@ -84,39 +89,32 @@ public class RequestSigner { .build(); } - public static void sign(AbstractMessage.Builder request, ECDsa key) { - var meta = (AbstractMessage) request.getField(request.getDescriptorForType().findFieldByName("meta_header")); - var body = (AbstractMessage) request.getField(request.getDescriptorForType().findFieldByName("body")); - var verify = (AbstractMessage) request.getField(request.getDescriptorForType().findFieldByName("verify_header")); + public static void sign(Message.Builder request, ECDsa key) { + var meta = getField(request, META_HEADER_FIELD_NAME); + var body = getField(request, BODY_FIELD_NAME); + var verify = getField(request, VERIFY_HEADER_FIELD_NAME); + var verifyOrigin = getField(verify, ORIGIN_FIELD_NAME); - AbstractMessage.Builder verifyBuilder; + Message.Builder verifyBuilder; if (verify instanceof Types.RequestVerificationHeader) { verifyBuilder = Types.RequestVerificationHeader.newBuilder(); } else if (verify instanceof Types.ResponseVerificationHeader) { verifyBuilder = Types.ResponseVerificationHeader.newBuilder(); } else { - throw new IllegalArgumentException("Unsopported message type"); - } - - var verifyOrigin = (AbstractMessage) verify.getField(verify.getDescriptorForType().findFieldByName("origin")); - if (verifyOrigin.getSerializedSize() == 0) { - verifyBuilder.setField( - verifyBuilder.getDescriptorForType().findFieldByName("body_signature"), - signMessagePart(key, body) + throw new IllegalArgumentException( + String.format(ERROR_UNSUPPORTED_TYPE_TEMPLATE, verify.getClass().getName()) ); - } else { - verifyBuilder.setField(verifyBuilder.getDescriptorForType().findFieldByName("origin"), verifyOrigin); } - verifyBuilder.setField( - verifyBuilder.getDescriptorForType().findFieldByName("meta_signature"), - signMessagePart(key, meta) - ); - verifyBuilder.setField( - verifyBuilder.getDescriptorForType().findFieldByName("origin_signature"), - signMessagePart(key, verifyOrigin) - ); + if (verifyOrigin.getSerializedSize() == 0) { + setField(verifyBuilder, BODY_SIGNATURE_FIELD_NAME, signMessagePart(key, body)); + } else { + setField(verifyBuilder, ORIGIN_FIELD_NAME, verifyOrigin); + } - request.setField(request.getDescriptorForType().findFieldByName("verify_header"), verifyBuilder.build()); + setField(verifyBuilder, META_SIGNATURE_FIELD_NAME, signMessagePart(key, meta)); + setField(verifyBuilder, ORIGIN_SIGNATURE_FIELD_NAME, signMessagePart(key, verifyOrigin)); + setField(request, VERIFY_HEADER_FIELD_NAME, verifyBuilder.build()); } + } diff --git a/client/src/main/java/info/FrostFS/sdk/Verifier.java b/client/src/main/java/info/FrostFS/sdk/tools/Verifier.java similarity index 70% rename from client/src/main/java/info/FrostFS/sdk/Verifier.java rename to client/src/main/java/info/FrostFS/sdk/tools/Verifier.java index e0c37ec..14e7327 100644 --- a/client/src/main/java/info/FrostFS/sdk/Verifier.java +++ b/client/src/main/java/info/FrostFS/sdk/tools/Verifier.java @@ -1,7 +1,7 @@ -package info.FrostFS.sdk; +package info.FrostFS.sdk.tools; -import com.google.protobuf.AbstractMessage; -import frostFS.refs.Types; +import com.google.protobuf.Message; +import frostFS.session.Types; import info.FrostFS.sdk.mappers.StatusMapper; import org.apache.commons.codec.digest.DigestUtils; import org.bouncycastle.asn1.sec.SECNamedCurves; @@ -17,17 +17,20 @@ import java.security.Signature; import java.util.Arrays; import static info.FrostFS.sdk.KeyExtension.getPublicKeyFromBytes; +import static info.FrostFS.sdk.constants.CryptoConst.SIGNATURE_ALGORITHM; +import static info.FrostFS.sdk.constants.FieldConst.*; +import static info.FrostFS.sdk.tools.MessageHelper.getField; import static java.util.Objects.isNull; import static org.bouncycastle.crypto.util.DigestFactory.createSHA256; import static org.bouncycastle.util.BigIntegers.fromUnsignedByteArray; public class Verifier { + public static final String ERROR_WRONG_SIG_SIZE_TEMPLATE = "Wrong signature size. Expected length=%s, actual=%s"; + public static final String ERROR_INVALID_RESPONSE = "Invalid response"; public static final int RFC6979_SIG_SIZE = 64; - public static boolean verifyRFC6979(Types.SignatureRFC6979 signature, AbstractMessage message) { - return verifyRFC6979( - signature.getKey().toByteArray(), message.toByteArray(), signature.getSign().toByteArray() - ); + public static boolean verifyRFC6979(frostFS.refs.Types.SignatureRFC6979 signature, Message data) { + return verifyRFC6979(signature.getKey().toByteArray(), data.toByteArray(), signature.getSign().toByteArray()); } public static boolean verifyRFC6979(byte[] publicKey, byte[] data, byte[] sig) { @@ -49,8 +52,7 @@ public class Verifier { private static BigInteger[] decodeSignature(byte[] sig) { if (sig.length != RFC6979_SIG_SIZE) { throw new IllegalArgumentException( - String.format("Wrong signature size. Expected length=%s, actual=%s", - RFC6979_SIG_SIZE, sig.length) + String.format(ERROR_WRONG_SIG_SIZE_TEMPLATE, RFC6979_SIG_SIZE, sig.length) ); } @@ -61,42 +63,40 @@ public class Verifier { return rs; } - public static void checkResponse(AbstractMessage response) { + public static void checkResponse(Message response) { if (!verify(response)) { - throw new IllegalArgumentException("Invalid response"); + throw new IllegalArgumentException(ERROR_INVALID_RESPONSE); } - var metaHeader = (frostFS.session.Types.ResponseMetaHeader) response - .getField(response.getDescriptorForType().findFieldByName("meta_header")); + + var metaHeader = (Types.ResponseMetaHeader) getField(response, META_HEADER_FIELD_NAME); var status = StatusMapper.toModel(metaHeader.getStatus()); if (!status.isSuccess()) { throw new IllegalArgumentException(status.toString()); } } - public static boolean verify(AbstractMessage message) { - var body = (AbstractMessage) message.getField(message.getDescriptorForType().findFieldByName("body")); - var metaHeader = (frostFS.session.Types.ResponseMetaHeader) - message.getField(message.getDescriptorForType().findFieldByName("meta_header")); - var verifyHeader = (frostFS.session.Types.ResponseVerificationHeader) - message.getField(message.getDescriptorForType().findFieldByName("verify_header")); + public static boolean verify(Message response) { + var body = getField(response, BODY_FIELD_NAME); + var metaHeader = (Types.ResponseMetaHeader) getField(response, META_HEADER_FIELD_NAME); + var verifyHeader = (Types.ResponseVerificationHeader) getField(response, VERIFY_HEADER_FIELD_NAME); return verifyMatryoshkaLevel(body, metaHeader, verifyHeader); } - public static boolean verifyMatryoshkaLevel(AbstractMessage body, + public static boolean verifyMatryoshkaLevel(Message data, frostFS.session.Types.ResponseMetaHeader meta, frostFS.session.Types.ResponseVerificationHeader verification) { if (!verifyMessagePart(verification.getMetaSignature(), meta)) return false; var origin = verification.getOrigin(); if (!verifyMessagePart(verification.getOriginSignature(), origin)) return false; if (origin.getSerializedSize() == 0) { - return verifyMessagePart(verification.getBodySignature(), body); + return verifyMessagePart(verification.getBodySignature(), data); } return verification.getBodySignature().getSerializedSize() == 0 - && verifyMatryoshkaLevel(body, meta.getOrigin(), origin); + && verifyMatryoshkaLevel(data, meta.getOrigin(), origin); } - public static boolean verifyMessagePart(Types.Signature sig, AbstractMessage data) { + public static boolean verifyMessagePart(frostFS.refs.Types.Signature sig, Message data) { if (sig.getSerializedSize() == 0 || sig.getKey().isEmpty() || sig.getSign().isEmpty()) return false; var publicKey = getPublicKeyFromBytes(sig.getKey().toByteArray()); @@ -107,7 +107,7 @@ public class Verifier { public static boolean verifyData(PublicKey publicKey, byte[] data, byte[] sig) { try { - Signature signature = Signature.getInstance("NONEwithECDSAinP1363Format"); + Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); signature.initVerify(publicKey); signature.update(DigestUtils.sha512(data)); return signature.verify(Arrays.copyOfRange(sig, 1, sig.length)); diff --git a/cryptography/pom.xml b/cryptography/pom.xml index 9c18d69..f92accd 100644 --- a/cryptography/pom.xml +++ b/cryptography/pom.xml @@ -1,6 +1,6 @@ - 4.0.0 diff --git a/cryptography/src/main/java/info/FrostFS/sdk/Helper.java b/cryptography/src/main/java/info/FrostFS/sdk/Helper.java index fb11ac2..083121b 100644 --- a/cryptography/src/main/java/info/FrostFS/sdk/Helper.java +++ b/cryptography/src/main/java/info/FrostFS/sdk/Helper.java @@ -1,7 +1,7 @@ package info.FrostFS.sdk; -import com.google.protobuf.AbstractMessage; import com.google.protobuf.ByteString; +import com.google.protobuf.Message; import org.bouncycastle.crypto.digests.RIPEMD160Digest; import java.math.BigInteger; @@ -30,7 +30,7 @@ public class Helper { return getSha256Instance().digest(value); } - public static ByteString getSha256(AbstractMessage value) { + public static ByteString getSha256(Message value) { return ByteString.copyFrom(getSha256(value.toByteArray())); } diff --git a/models/pom.xml b/models/pom.xml index 7f9af60..1f04f5e 100644 --- a/models/pom.xml +++ b/models/pom.xml @@ -1,6 +1,6 @@ - 4.0.0 diff --git a/models/src/main/java/info/FrostFS/sdk/constants/FieldConst.java b/models/src/main/java/info/FrostFS/sdk/constants/FieldConst.java new file mode 100644 index 0000000..761258a --- /dev/null +++ b/models/src/main/java/info/FrostFS/sdk/constants/FieldConst.java @@ -0,0 +1,11 @@ +package info.FrostFS.sdk.constants; + +public class FieldConst { + public static final String META_HEADER_FIELD_NAME = "meta_header"; + public static final String META_SIGNATURE_FIELD_NAME = "meta_signature"; + public static final String BODY_FIELD_NAME = "body"; + public static final String BODY_SIGNATURE_FIELD_NAME = "body_signature"; + public static final String ORIGIN_FIELD_NAME = "origin"; + public static final String ORIGIN_SIGNATURE_FIELD_NAME = "origin_signature"; + public static final String VERIFY_HEADER_FIELD_NAME = "verify_header"; +} From bf2f19f08dcd36ff2cb9dd6591e759c4c2e50945 Mon Sep 17 00:00:00 2001 From: Bruk Ori Date: Tue, 23 Jul 2024 22:24:17 +0300 Subject: [PATCH 07/38] [#1] provide naming conventions Signed-off-by: Ori Bruk --- README.md | 28 +++++----- client/pom.xml | 8 +-- .../FrostFS/sdk/services/NetmapClient.java | 13 ----- .../FrostFS/sdk/services/ToolsClient.java | 8 --- .../sdk/constants/CryptoConst.java | 2 +- .../sdk/jdo/ClientEnvironment.java | 8 +-- .../sdk/jdo/ClientSettings.java | 2 +- .../{FrostFS => frostfs}/sdk/jdo/ECDsa.java | 4 +- .../sdk/jdo/NetworkSettings.java | 2 +- .../sdk/jdo/PutObjectParameters.java | 4 +- .../sdk/services/ContainerClient.java | 6 +- .../sdk/services/ContextAccessor.java | 4 +- .../sdk/services/FrostFSClient.java | 36 ++++++------ .../frostfs/sdk/services/NetmapClient.java | 13 +++++ .../sdk/services/ObjectClient.java | 14 ++--- .../sdk/services/SessionClient.java | 4 +- .../frostfs/sdk/services/ToolsClient.java | 8 +++ .../sdk/services/impl/ContainerService.java | 53 +++++++++--------- .../sdk/services/impl/NetmapService.java | 30 +++++----- .../sdk/services/impl/ObjectReader.java | 8 +-- .../sdk/services/impl/ObjectService.java | 56 +++++++++---------- .../sdk/services/impl/ObjectTools.java | 44 +++++++-------- .../sdk/services/impl/ObjectWriter.java | 6 +- .../sdk/services/impl/SearchReader.java | 8 +-- .../sdk/services/impl/SessionService.java | 27 ++++----- .../sdk/tools/GrpcClient.java | 2 +- .../sdk/tools/MessageHelper.java | 2 +- .../sdk/tools/RequestConstructor.java | 22 ++++---- .../sdk/tools/RequestSigner.java | 44 +++++++-------- .../sdk/tools/Verifier.java | 31 +++++----- cryptography/pom.xml | 4 +- .../{FrostFS => frostfs}/sdk/ArrayHelper.java | 2 +- .../info/{FrostFS => frostfs}/sdk/Base58.java | 6 +- .../info/{FrostFS => frostfs}/sdk/Helper.java | 2 +- .../sdk/KeyExtension.java | 6 +- models/pom.xml | 8 +-- .../sdk/UUIDExtension.java | 2 +- .../sdk/constants/AppConst.java | 2 +- .../sdk/constants/FieldConst.java | 2 +- .../sdk/constants/XHeaderConst.java | 2 +- .../sdk/dto/MetaHeader.java | 2 +- .../{FrostFS => frostfs}/sdk/dto/OwnerId.java | 6 +- .../sdk/dto/SessionToken.java | 2 +- .../sdk/dto/Signature.java | 4 +- .../{FrostFS => frostfs}/sdk/dto/Split.java | 6 +- .../{FrostFS => frostfs}/sdk/dto/SplitId.java | 6 +- .../{FrostFS => frostfs}/sdk/dto/Status.java | 4 +- .../{FrostFS => frostfs}/sdk/dto/Version.java | 2 +- .../sdk/dto/container/Container.java | 8 +-- .../sdk/dto/container/ContainerId.java | 6 +- .../sdk/dto/netmap/NetmapSnapshot.java | 2 +- .../sdk/dto/netmap/NodeInfo.java | 6 +- .../sdk/dto/netmap/PlacementPolicy.java | 2 +- .../sdk/dto/netmap/Replica.java | 2 +- .../sdk/dto/object/LargeObject.java | 6 +- .../sdk/dto/object/LinkObject.java | 8 +-- .../sdk/dto/object/ObjectAttribute.java | 2 +- .../sdk/dto/object/ObjectFilter.java | 8 +-- .../sdk/dto/object/ObjectFrostFS.java | 8 +-- .../sdk/dto/object/ObjectHeader.java | 12 ++-- .../sdk/dto/object/ObjectId.java | 6 +- .../sdk/enums/BasicAcl.java | 2 +- .../sdk/enums/NodeState.java | 2 +- .../sdk/enums/ObjectMatchType.java | 2 +- .../sdk/enums/ObjectType.java | 2 +- .../sdk/enums/SignatureScheme.java | 2 +- .../sdk/enums/StatusCode.java | 2 +- .../sdk/mappers/MetaHeaderMapper.java | 6 +- .../sdk/mappers/OwnerIdMapper.java | 6 +- .../sdk/mappers/SessionMapper.java | 4 +- .../sdk/mappers/SignatureMapper.java | 6 +- .../sdk/mappers/StatusMapper.java | 8 +-- .../sdk/mappers/VersionMapper.java | 6 +- .../mappers/container/ContainerIdMapper.java | 6 +- .../mappers/container/ContainerMapper.java | 16 +++--- .../mappers/netmap/NetmapSnapshotMapper.java | 6 +- .../sdk/mappers/netmap/NodeInfoMapper.java | 16 +++--- .../mappers/netmap/PlacementPolicyMapper.java | 8 +-- .../sdk/mappers/netmap/ReplicaMapper.java | 6 +- .../mappers/object/ObjectAttributeMapper.java | 6 +- .../mappers/object/ObjectFilterMapper.java | 8 +-- .../mappers/object/ObjectFrostFSMapper.java | 8 +-- .../mappers/object/ObjectHeaderMapper.java | 16 +++--- .../sdk/mappers/object/ObjectIdMapper.java | 6 +- pom.xml | 4 +- protos/pom.xml | 4 +- .../src/main/proto/accounting/service.proto | 2 +- protos/src/main/proto/accounting/types.proto | 2 +- protos/src/main/proto/acl/types.proto | 2 +- .../src/main/proto/apemanager/service.proto | 2 +- protos/src/main/proto/apemanager/types.proto | 2 +- protos/src/main/proto/container/service.proto | 2 +- protos/src/main/proto/container/types.proto | 2 +- protos/src/main/proto/lock/types.proto | 2 +- protos/src/main/proto/netmap/service.proto | 2 +- protos/src/main/proto/netmap/types.proto | 2 +- protos/src/main/proto/object/service.proto | 2 +- protos/src/main/proto/object/types.proto | 2 +- protos/src/main/proto/refs/types.proto | 2 +- protos/src/main/proto/session/service.proto | 2 +- protos/src/main/proto/session/types.proto | 2 +- protos/src/main/proto/status/types.proto | 2 +- protos/src/main/proto/tombstone/types.proto | 2 +- 103 files changed, 416 insertions(+), 417 deletions(-) delete mode 100644 client/src/main/java/info/FrostFS/sdk/services/NetmapClient.java delete mode 100644 client/src/main/java/info/FrostFS/sdk/services/ToolsClient.java rename client/src/main/java/info/{FrostFS => frostfs}/sdk/constants/CryptoConst.java (75%) rename client/src/main/java/info/{FrostFS => frostfs}/sdk/jdo/ClientEnvironment.java (88%) rename client/src/main/java/info/{FrostFS => frostfs}/sdk/jdo/ClientSettings.java (97%) rename client/src/main/java/info/{FrostFS => frostfs}/sdk/jdo/ECDsa.java (88%) rename client/src/main/java/info/{FrostFS => frostfs}/sdk/jdo/NetworkSettings.java (99%) rename client/src/main/java/info/{FrostFS => frostfs}/sdk/jdo/PutObjectParameters.java (96%) rename client/src/main/java/info/{FrostFS => frostfs}/sdk/services/ContainerClient.java (64%) rename client/src/main/java/info/{FrostFS => frostfs}/sdk/services/ContextAccessor.java (75%) rename client/src/main/java/info/{FrostFS => frostfs}/sdk/services/FrostFSClient.java (82%) create mode 100644 client/src/main/java/info/frostfs/sdk/services/NetmapClient.java rename client/src/main/java/info/{FrostFS => frostfs}/sdk/services/ObjectClient.java (54%) rename client/src/main/java/info/{FrostFS => frostfs}/sdk/services/SessionClient.java (52%) create mode 100644 client/src/main/java/info/frostfs/sdk/services/ToolsClient.java rename client/src/main/java/info/{FrostFS => frostfs}/sdk/services/impl/ContainerService.java (68%) rename client/src/main/java/info/{FrostFS => frostfs}/sdk/services/impl/NetmapService.java (86%) rename client/src/main/java/info/{FrostFS => frostfs}/sdk/services/impl/ObjectReader.java (91%) rename client/src/main/java/info/{FrostFS => frostfs}/sdk/services/impl/ObjectService.java (90%) rename client/src/main/java/info/{FrostFS => frostfs}/sdk/services/impl/ObjectTools.java (75%) rename client/src/main/java/info/{FrostFS => frostfs}/sdk/services/impl/ObjectWriter.java (93%) rename client/src/main/java/info/{FrostFS => frostfs}/sdk/services/impl/SearchReader.java (72%) rename client/src/main/java/info/{FrostFS => frostfs}/sdk/services/impl/SessionService.java (75%) rename client/src/main/java/info/{FrostFS => frostfs}/sdk/tools/GrpcClient.java (96%) rename client/src/main/java/info/{FrostFS => frostfs}/sdk/tools/MessageHelper.java (94%) rename client/src/main/java/info/{FrostFS => frostfs}/sdk/tools/RequestConstructor.java (74%) rename client/src/main/java/info/{FrostFS => frostfs}/sdk/tools/RequestSigner.java (72%) rename client/src/main/java/info/{FrostFS => frostfs}/sdk/tools/Verifier.java (80%) rename cryptography/src/main/java/info/{FrostFS => frostfs}/sdk/ArrayHelper.java (93%) rename cryptography/src/main/java/info/{FrostFS => frostfs}/sdk/Base58.java (97%) rename cryptography/src/main/java/info/{FrostFS => frostfs}/sdk/Helper.java (97%) rename cryptography/src/main/java/info/{FrostFS => frostfs}/sdk/KeyExtension.java (98%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/UUIDExtension.java (95%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/constants/AppConst.java (79%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/constants/FieldConst.java (93%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/constants/XHeaderConst.java (89%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/dto/MetaHeader.java (96%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/dto/OwnerId.java (75%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/dto/SessionToken.java (91%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/dto/Signature.java (87%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/dto/Split.java (91%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/dto/SplitId.java (85%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/dto/Status.java (92%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/dto/Version.java (95%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/dto/container/Container.java (86%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/dto/container/ContainerId.java (85%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/dto/netmap/NetmapSnapshot.java (92%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/dto/netmap/NodeInfo.java (89%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/dto/netmap/PlacementPolicy.java (90%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/dto/netmap/Replica.java (94%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/dto/object/LargeObject.java (81%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/dto/object/LinkObject.java (74%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/dto/object/ObjectAttribute.java (92%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/dto/object/ObjectFilter.java (90%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/dto/object/ObjectFrostFS.java (92%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/dto/object/ObjectHeader.java (91%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/dto/object/ObjectId.java (83%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/enums/BasicAcl.java (95%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/enums/NodeState.java (95%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/enums/ObjectMatchType.java (87%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/enums/ObjectType.java (95%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/enums/SignatureScheme.java (87%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/enums/StatusCode.java (97%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/mappers/MetaHeaderMapper.java (78%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/mappers/OwnerIdMapper.java (73%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/mappers/SessionMapper.java (93%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/mappers/SignatureMapper.java (87%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/mappers/StatusMapper.java (78%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/mappers/VersionMapper.java (80%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/mappers/container/ContainerIdMapper.java (71%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/mappers/container/ContainerMapper.java (75%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/mappers/netmap/NetmapSnapshotMapper.java (80%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/mappers/netmap/NodeInfoMapper.java (72%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/mappers/netmap/PlacementPolicyMapper.java (80%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/mappers/netmap/ReplicaMapper.java (78%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/mappers/object/ObjectAttributeMapper.java (79%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/mappers/object/ObjectFilterMapper.java (82%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/mappers/object/ObjectFrostFSMapper.java (67%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/mappers/object/ObjectHeaderMapper.java (80%) rename models/src/main/java/info/{FrostFS => frostfs}/sdk/mappers/object/ObjectIdMapper.java (78%) diff --git a/README.md b/README.md index fec8945..ff4652e 100644 --- a/README.md +++ b/README.md @@ -21,12 +21,12 @@ neo-go wallet export -w -d ### Container ```java -import info.FrostFS.sdk.dto.container.Container; -import info.FrostFS.sdk.dto.netmap.PlacementPolicy; -import info.FrostFS.sdk.dto.netmap.Replica; -import info.FrostFS.sdk.enums.BasicAcl; -import info.FrostFS.sdk.jdo.ClientSettings; -import info.FrostFS.sdk.services.FrostFSClient; +import info.frostfs.sdk.dto.container.Container; +import info.frostfs.sdk.dto.netmap.PlacementPolicy; +import info.frostfs.sdk.dto.netmap.Replica; +import info.frostfs.sdk.enums.BasicAcl; +import info.frostfs.sdk.jdo.ClientSettings; +import info.frostfs.sdk.services.FrostFSClient; public class ContainerExample { @@ -53,13 +53,13 @@ public class ContainerExample { ### Object ```java -import info.FrostFS.sdk.enums.ObjectType; -import info.FrostFS.sdk.dto.container.ContainerId; -import info.FrostFS.sdk.dto.object.ObjectAttribute; -import info.FrostFS.sdk.dto.object.ObjectFilter; -import info.FrostFS.sdk.dto.object.ObjectHeader; -import info.FrostFS.sdk.jdo.PutObjectParameters; -import info.FrostFS.sdk.services.FrostFSClient; +import info.frostfs.sdk.enums.ObjectType; +import info.frostfs.sdk.dto.container.ContainerId; +import info.frostfs.sdk.dto.object.ObjectAttribute; +import info.frostfs.sdk.dto.object.ObjectFilter; +import info.frostfs.sdk.dto.object.ObjectHeader; +import info.frostfs.sdk.jdo.PutObjectParameters; +import info.frostfs.sdk.services.FrostFSClient; import java.io.FileInputStream; import java.io.IOException; @@ -71,7 +71,7 @@ public class ObjectExample { FrostFSClient frostFSClient = new FrostFSClient(clientSettings); // Put object - info.FrostFS.sdk.dto.object.ObjectId objectId; + info.frostfs.sdk.dto.object.ObjectId objectId; try (FileInputStream fis = new FileInputStream("cat.jpg")) { var cat = new ObjectHeader( containerId, ObjectType.REGULAR, new ObjectAttribute[]{new ObjectAttribute("Filename", "cat.jpg")} diff --git a/client/pom.xml b/client/pom.xml index 3d3426e..917fc46 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -4,8 +4,8 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - info.FrostFS.sdk - FrostFS-sdk-java + info.frostfs.sdk + frostfs-sdk-java 0.1.0 @@ -19,12 +19,12 @@ - info.FrostFS.sdk + info.frostfs.sdk cryptography 0.1.0 - info.FrostFS.sdk + info.frostfs.sdk models 0.1.0 diff --git a/client/src/main/java/info/FrostFS/sdk/services/NetmapClient.java b/client/src/main/java/info/FrostFS/sdk/services/NetmapClient.java deleted file mode 100644 index f053dfa..0000000 --- a/client/src/main/java/info/FrostFS/sdk/services/NetmapClient.java +++ /dev/null @@ -1,13 +0,0 @@ -package info.FrostFS.sdk.services; - -import info.FrostFS.sdk.dto.netmap.NetmapSnapshot; -import info.FrostFS.sdk.dto.netmap.NodeInfo; -import info.FrostFS.sdk.jdo.NetworkSettings; - -public interface NetmapClient { - NetmapSnapshot getNetmapSnapshot(); - - NodeInfo getLocalNodeInfo(); - - NetworkSettings getNetworkSettings(); -} diff --git a/client/src/main/java/info/FrostFS/sdk/services/ToolsClient.java b/client/src/main/java/info/FrostFS/sdk/services/ToolsClient.java deleted file mode 100644 index fb673a2..0000000 --- a/client/src/main/java/info/FrostFS/sdk/services/ToolsClient.java +++ /dev/null @@ -1,8 +0,0 @@ -package info.FrostFS.sdk.services; - -import info.FrostFS.sdk.dto.object.ObjectHeader; -import info.FrostFS.sdk.dto.object.ObjectId; - -public interface ToolsClient { - ObjectId calculateObjectId(ObjectHeader header); -} diff --git a/client/src/main/java/info/FrostFS/sdk/constants/CryptoConst.java b/client/src/main/java/info/frostfs/sdk/constants/CryptoConst.java similarity index 75% rename from client/src/main/java/info/FrostFS/sdk/constants/CryptoConst.java rename to client/src/main/java/info/frostfs/sdk/constants/CryptoConst.java index 742ef60..84db5c9 100644 --- a/client/src/main/java/info/FrostFS/sdk/constants/CryptoConst.java +++ b/client/src/main/java/info/frostfs/sdk/constants/CryptoConst.java @@ -1,4 +1,4 @@ -package info.FrostFS.sdk.constants; +package info.frostfs.sdk.constants; public class CryptoConst { public static final String SIGNATURE_ALGORITHM = "NONEwithECDSAinP1363Format"; diff --git a/client/src/main/java/info/FrostFS/sdk/jdo/ClientEnvironment.java b/client/src/main/java/info/frostfs/sdk/jdo/ClientEnvironment.java similarity index 88% rename from client/src/main/java/info/FrostFS/sdk/jdo/ClientEnvironment.java rename to client/src/main/java/info/frostfs/sdk/jdo/ClientEnvironment.java index f770057..2165f06 100644 --- a/client/src/main/java/info/FrostFS/sdk/jdo/ClientEnvironment.java +++ b/client/src/main/java/info/frostfs/sdk/jdo/ClientEnvironment.java @@ -1,8 +1,8 @@ -package info.FrostFS.sdk.jdo; +package info.frostfs.sdk.jdo; -import info.FrostFS.sdk.dto.OwnerId; -import info.FrostFS.sdk.dto.Version; -import info.FrostFS.sdk.services.FrostFSClient; +import info.frostfs.sdk.dto.OwnerId; +import info.frostfs.sdk.dto.Version; +import info.frostfs.sdk.services.FrostFSClient; import io.grpc.Channel; public class ClientEnvironment { diff --git a/client/src/main/java/info/FrostFS/sdk/jdo/ClientSettings.java b/client/src/main/java/info/frostfs/sdk/jdo/ClientSettings.java similarity index 97% rename from client/src/main/java/info/FrostFS/sdk/jdo/ClientSettings.java rename to client/src/main/java/info/frostfs/sdk/jdo/ClientSettings.java index c7f5de0..03b83ad 100644 --- a/client/src/main/java/info/FrostFS/sdk/jdo/ClientSettings.java +++ b/client/src/main/java/info/frostfs/sdk/jdo/ClientSettings.java @@ -1,4 +1,4 @@ -package info.FrostFS.sdk.jdo; +package info.frostfs.sdk.jdo; import io.grpc.ChannelCredentials; import org.apache.commons.lang3.StringUtils; diff --git a/client/src/main/java/info/FrostFS/sdk/jdo/ECDsa.java b/client/src/main/java/info/frostfs/sdk/jdo/ECDsa.java similarity index 88% rename from client/src/main/java/info/FrostFS/sdk/jdo/ECDsa.java rename to client/src/main/java/info/frostfs/sdk/jdo/ECDsa.java index e3afee7..060eef9 100644 --- a/client/src/main/java/info/FrostFS/sdk/jdo/ECDsa.java +++ b/client/src/main/java/info/frostfs/sdk/jdo/ECDsa.java @@ -1,8 +1,8 @@ -package info.FrostFS.sdk.jdo; +package info.frostfs.sdk.jdo; import java.security.PrivateKey; -import static info.FrostFS.sdk.KeyExtension.*; +import static info.frostfs.sdk.KeyExtension.*; public class ECDsa { private final byte[] publicKeyByte; diff --git a/client/src/main/java/info/FrostFS/sdk/jdo/NetworkSettings.java b/client/src/main/java/info/frostfs/sdk/jdo/NetworkSettings.java similarity index 99% rename from client/src/main/java/info/FrostFS/sdk/jdo/NetworkSettings.java rename to client/src/main/java/info/frostfs/sdk/jdo/NetworkSettings.java index 680d3d3..1ed01a6 100644 --- a/client/src/main/java/info/FrostFS/sdk/jdo/NetworkSettings.java +++ b/client/src/main/java/info/frostfs/sdk/jdo/NetworkSettings.java @@ -1,4 +1,4 @@ -package info.FrostFS.sdk.jdo; +package info.frostfs.sdk.jdo; import java.util.HashMap; import java.util.Map; diff --git a/client/src/main/java/info/FrostFS/sdk/jdo/PutObjectParameters.java b/client/src/main/java/info/frostfs/sdk/jdo/PutObjectParameters.java similarity index 96% rename from client/src/main/java/info/FrostFS/sdk/jdo/PutObjectParameters.java rename to client/src/main/java/info/frostfs/sdk/jdo/PutObjectParameters.java index 51082a3..cd3338d 100644 --- a/client/src/main/java/info/FrostFS/sdk/jdo/PutObjectParameters.java +++ b/client/src/main/java/info/frostfs/sdk/jdo/PutObjectParameters.java @@ -1,6 +1,6 @@ -package info.FrostFS.sdk.jdo; +package info.frostfs.sdk.jdo; -import info.FrostFS.sdk.dto.object.ObjectHeader; +import info.frostfs.sdk.dto.object.ObjectHeader; import java.io.FileInputStream; diff --git a/client/src/main/java/info/FrostFS/sdk/services/ContainerClient.java b/client/src/main/java/info/frostfs/sdk/services/ContainerClient.java similarity index 64% rename from client/src/main/java/info/FrostFS/sdk/services/ContainerClient.java rename to client/src/main/java/info/frostfs/sdk/services/ContainerClient.java index 385f616..d3f7f9c 100644 --- a/client/src/main/java/info/FrostFS/sdk/services/ContainerClient.java +++ b/client/src/main/java/info/frostfs/sdk/services/ContainerClient.java @@ -1,7 +1,7 @@ -package info.FrostFS.sdk.services; +package info.frostfs.sdk.services; -import info.FrostFS.sdk.dto.container.Container; -import info.FrostFS.sdk.dto.container.ContainerId; +import info.frostfs.sdk.dto.container.Container; +import info.frostfs.sdk.dto.container.ContainerId; import java.util.List; diff --git a/client/src/main/java/info/FrostFS/sdk/services/ContextAccessor.java b/client/src/main/java/info/frostfs/sdk/services/ContextAccessor.java similarity index 75% rename from client/src/main/java/info/FrostFS/sdk/services/ContextAccessor.java rename to client/src/main/java/info/frostfs/sdk/services/ContextAccessor.java index 58f9c43..72c47dd 100644 --- a/client/src/main/java/info/FrostFS/sdk/services/ContextAccessor.java +++ b/client/src/main/java/info/frostfs/sdk/services/ContextAccessor.java @@ -1,6 +1,6 @@ -package info.FrostFS.sdk.services; +package info.frostfs.sdk.services; -import info.FrostFS.sdk.jdo.ClientEnvironment; +import info.frostfs.sdk.jdo.ClientEnvironment; public class ContextAccessor { private final ClientEnvironment context; diff --git a/client/src/main/java/info/FrostFS/sdk/services/FrostFSClient.java b/client/src/main/java/info/frostfs/sdk/services/FrostFSClient.java similarity index 82% rename from client/src/main/java/info/FrostFS/sdk/services/FrostFSClient.java rename to client/src/main/java/info/frostfs/sdk/services/FrostFSClient.java index b0c7f1b..da87a20 100644 --- a/client/src/main/java/info/FrostFS/sdk/services/FrostFSClient.java +++ b/client/src/main/java/info/frostfs/sdk/services/FrostFSClient.java @@ -1,26 +1,26 @@ -package info.FrostFS.sdk.services; +package info.frostfs.sdk.services; -import frostFS.session.Types; -import info.FrostFS.sdk.dto.SessionToken; -import info.FrostFS.sdk.dto.Version; -import info.FrostFS.sdk.dto.container.Container; -import info.FrostFS.sdk.dto.container.ContainerId; -import info.FrostFS.sdk.dto.netmap.NetmapSnapshot; -import info.FrostFS.sdk.dto.netmap.NodeInfo; -import info.FrostFS.sdk.dto.object.ObjectFilter; -import info.FrostFS.sdk.dto.object.ObjectFrostFS; -import info.FrostFS.sdk.dto.object.ObjectHeader; -import info.FrostFS.sdk.dto.object.ObjectId; -import info.FrostFS.sdk.jdo.ClientEnvironment; -import info.FrostFS.sdk.jdo.ClientSettings; -import info.FrostFS.sdk.jdo.NetworkSettings; -import info.FrostFS.sdk.jdo.PutObjectParameters; -import info.FrostFS.sdk.services.impl.*; +import frostfs.session.Types; +import info.frostfs.sdk.dto.SessionToken; +import info.frostfs.sdk.dto.Version; +import info.frostfs.sdk.dto.container.Container; +import info.frostfs.sdk.dto.container.ContainerId; +import info.frostfs.sdk.dto.netmap.NetmapSnapshot; +import info.frostfs.sdk.dto.netmap.NodeInfo; +import info.frostfs.sdk.dto.object.ObjectFilter; +import info.frostfs.sdk.dto.object.ObjectFrostFS; +import info.frostfs.sdk.dto.object.ObjectHeader; +import info.frostfs.sdk.dto.object.ObjectId; +import info.frostfs.sdk.jdo.ClientEnvironment; +import info.frostfs.sdk.jdo.ClientSettings; +import info.frostfs.sdk.jdo.NetworkSettings; +import info.frostfs.sdk.jdo.PutObjectParameters; +import info.frostfs.sdk.services.impl.*; import io.grpc.Channel; import java.util.List; -import static info.FrostFS.sdk.tools.GrpcClient.initGrpcChannel; +import static info.frostfs.sdk.tools.GrpcClient.initGrpcChannel; import static java.util.Objects.isNull; public class FrostFSClient implements ContainerClient, ObjectClient, NetmapClient, SessionClient, ToolsClient { diff --git a/client/src/main/java/info/frostfs/sdk/services/NetmapClient.java b/client/src/main/java/info/frostfs/sdk/services/NetmapClient.java new file mode 100644 index 0000000..bc34978 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/services/NetmapClient.java @@ -0,0 +1,13 @@ +package info.frostfs.sdk.services; + +import info.frostfs.sdk.dto.netmap.NetmapSnapshot; +import info.frostfs.sdk.dto.netmap.NodeInfo; +import info.frostfs.sdk.jdo.NetworkSettings; + +public interface NetmapClient { + NetmapSnapshot getNetmapSnapshot(); + + NodeInfo getLocalNodeInfo(); + + NetworkSettings getNetworkSettings(); +} diff --git a/client/src/main/java/info/FrostFS/sdk/services/ObjectClient.java b/client/src/main/java/info/frostfs/sdk/services/ObjectClient.java similarity index 54% rename from client/src/main/java/info/FrostFS/sdk/services/ObjectClient.java rename to client/src/main/java/info/frostfs/sdk/services/ObjectClient.java index 96ae2a3..423e938 100644 --- a/client/src/main/java/info/FrostFS/sdk/services/ObjectClient.java +++ b/client/src/main/java/info/frostfs/sdk/services/ObjectClient.java @@ -1,11 +1,11 @@ -package info.FrostFS.sdk.services; +package info.frostfs.sdk.services; -import info.FrostFS.sdk.dto.container.ContainerId; -import info.FrostFS.sdk.dto.object.ObjectFilter; -import info.FrostFS.sdk.dto.object.ObjectFrostFS; -import info.FrostFS.sdk.dto.object.ObjectHeader; -import info.FrostFS.sdk.dto.object.ObjectId; -import info.FrostFS.sdk.jdo.PutObjectParameters; +import info.frostfs.sdk.dto.container.ContainerId; +import info.frostfs.sdk.dto.object.ObjectFilter; +import info.frostfs.sdk.dto.object.ObjectFrostFS; +import info.frostfs.sdk.dto.object.ObjectHeader; +import info.frostfs.sdk.dto.object.ObjectId; +import info.frostfs.sdk.jdo.PutObjectParameters; public interface ObjectClient { ObjectHeader getObjectHead(ContainerId containerId, ObjectId objectId); diff --git a/client/src/main/java/info/FrostFS/sdk/services/SessionClient.java b/client/src/main/java/info/frostfs/sdk/services/SessionClient.java similarity index 52% rename from client/src/main/java/info/FrostFS/sdk/services/SessionClient.java rename to client/src/main/java/info/frostfs/sdk/services/SessionClient.java index 6fd7f4f..16e0da0 100644 --- a/client/src/main/java/info/FrostFS/sdk/services/SessionClient.java +++ b/client/src/main/java/info/frostfs/sdk/services/SessionClient.java @@ -1,6 +1,6 @@ -package info.FrostFS.sdk.services; +package info.frostfs.sdk.services; -import info.FrostFS.sdk.dto.SessionToken; +import info.frostfs.sdk.dto.SessionToken; public interface SessionClient { SessionToken createSession(long expiration); diff --git a/client/src/main/java/info/frostfs/sdk/services/ToolsClient.java b/client/src/main/java/info/frostfs/sdk/services/ToolsClient.java new file mode 100644 index 0000000..e8ce342 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/services/ToolsClient.java @@ -0,0 +1,8 @@ +package info.frostfs.sdk.services; + +import info.frostfs.sdk.dto.object.ObjectHeader; +import info.frostfs.sdk.dto.object.ObjectId; + +public interface ToolsClient { + ObjectId calculateObjectId(ObjectHeader header); +} diff --git a/client/src/main/java/info/FrostFS/sdk/services/impl/ContainerService.java b/client/src/main/java/info/frostfs/sdk/services/impl/ContainerService.java similarity index 68% rename from client/src/main/java/info/FrostFS/sdk/services/impl/ContainerService.java rename to client/src/main/java/info/frostfs/sdk/services/impl/ContainerService.java index a1df64c..1e2f5e7 100644 --- a/client/src/main/java/info/FrostFS/sdk/services/impl/ContainerService.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/ContainerService.java @@ -1,24 +1,25 @@ -package info.FrostFS.sdk.services.impl; +package info.frostfs.sdk.services.impl; -import frostFS.container.ContainerServiceGrpc; -import frostFS.container.Service; -import info.FrostFS.sdk.dto.container.Container; -import info.FrostFS.sdk.dto.container.ContainerId; -import info.FrostFS.sdk.jdo.ClientEnvironment; -import info.FrostFS.sdk.mappers.OwnerIdMapper; -import info.FrostFS.sdk.mappers.VersionMapper; -import info.FrostFS.sdk.mappers.container.ContainerIdMapper; -import info.FrostFS.sdk.mappers.container.ContainerMapper; -import info.FrostFS.sdk.services.ContainerClient; -import info.FrostFS.sdk.services.ContextAccessor; -import info.FrostFS.sdk.tools.Verifier; +import frostfs.container.ContainerServiceGrpc; +import frostfs.container.Service; +import info.frostfs.sdk.dto.container.Container; +import info.frostfs.sdk.dto.container.ContainerId; +import info.frostfs.sdk.jdo.ClientEnvironment; +import info.frostfs.sdk.mappers.OwnerIdMapper; +import info.frostfs.sdk.mappers.VersionMapper; +import info.frostfs.sdk.mappers.container.ContainerIdMapper; +import info.frostfs.sdk.mappers.container.ContainerMapper; +import info.frostfs.sdk.services.ContainerClient; +import info.frostfs.sdk.services.ContextAccessor; +import info.frostfs.sdk.tools.Verifier; +import info.frostfs.sdk.tools.RequestConstructor; +import info.frostfs.sdk.tools.RequestSigner; import java.util.List; import java.util.stream.Collectors; -import static info.FrostFS.sdk.tools.RequestConstructor.addMetaHeader; -import static info.FrostFS.sdk.tools.RequestSigner.sign; -import static info.FrostFS.sdk.tools.RequestSigner.signRFC6979; +import static info.frostfs.sdk.tools.RequestConstructor.addMetaHeader; +import static info.frostfs.sdk.tools.RequestSigner.signRFC6979; public class ContainerService extends ContextAccessor implements ContainerClient { private final ContainerServiceGrpc.ContainerServiceBlockingStub serviceBlockingStub; @@ -36,8 +37,8 @@ public class ContainerService extends ContextAccessor implements ContainerClient .build() ); - addMetaHeader(request); - sign(request, getContext().getKey()); + RequestConstructor.addMetaHeader(request); + RequestSigner.sign(request, getContext().getKey()); var response = serviceBlockingStub.get(request.build()); @@ -53,8 +54,8 @@ public class ContainerService extends ContextAccessor implements ContainerClient .build() ); - addMetaHeader(request); - sign(request, getContext().getKey()); + RequestConstructor.addMetaHeader(request); + RequestSigner.sign(request, getContext().getKey()); var response = serviceBlockingStub.list(request.build()); @@ -78,13 +79,13 @@ public class ContainerService extends ContextAccessor implements ContainerClient Service.PutRequest.Body.newBuilder() .setContainer(grpcContainer) .setSignature( - signRFC6979(getContext().getKey(), grpcContainer) + RequestSigner.signRFC6979(getContext().getKey(), grpcContainer) ) .build() ); - addMetaHeader(request); - sign(request, getContext().getKey()); + RequestConstructor.addMetaHeader(request); + RequestSigner.sign(request, getContext().getKey()); var response = serviceBlockingStub.put(request.build()); @@ -99,14 +100,14 @@ public class ContainerService extends ContextAccessor implements ContainerClient .setBody( Service.DeleteRequest.Body.newBuilder() .setContainerId(grpcContainerId) - .setSignature(signRFC6979( + .setSignature(RequestSigner.signRFC6979( getContext().getKey(), grpcContainerId.getValue() )) .build() ); - addMetaHeader(request); - sign(request, getContext().getKey()); + RequestConstructor.addMetaHeader(request); + RequestSigner.sign(request, getContext().getKey()); var response = serviceBlockingStub.delete(request.build()); diff --git a/client/src/main/java/info/FrostFS/sdk/services/impl/NetmapService.java b/client/src/main/java/info/frostfs/sdk/services/impl/NetmapService.java similarity index 86% rename from client/src/main/java/info/FrostFS/sdk/services/impl/NetmapService.java rename to client/src/main/java/info/frostfs/sdk/services/impl/NetmapService.java index 135e95b..19c5995 100644 --- a/client/src/main/java/info/FrostFS/sdk/services/impl/NetmapService.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/NetmapService.java @@ -1,22 +1,22 @@ -package info.FrostFS.sdk.services.impl; +package info.frostfs.sdk.services.impl; -import frostFS.netmap.NetmapServiceGrpc; -import frostFS.netmap.Service; -import frostFS.netmap.Types; -import info.FrostFS.sdk.dto.netmap.NetmapSnapshot; -import info.FrostFS.sdk.dto.netmap.NodeInfo; -import info.FrostFS.sdk.jdo.ClientEnvironment; -import info.FrostFS.sdk.jdo.NetworkSettings; -import info.FrostFS.sdk.mappers.netmap.NetmapSnapshotMapper; -import info.FrostFS.sdk.mappers.netmap.NodeInfoMapper; -import info.FrostFS.sdk.services.ContextAccessor; -import info.FrostFS.sdk.services.NetmapClient; -import info.FrostFS.sdk.tools.Verifier; +import frostfs.netmap.NetmapServiceGrpc; +import frostfs.netmap.Service; +import frostfs.netmap.Types; +import info.frostfs.sdk.dto.netmap.NetmapSnapshot; +import info.frostfs.sdk.dto.netmap.NodeInfo; +import info.frostfs.sdk.jdo.ClientEnvironment; +import info.frostfs.sdk.jdo.NetworkSettings; +import info.frostfs.sdk.mappers.netmap.NetmapSnapshotMapper; +import info.frostfs.sdk.mappers.netmap.NodeInfoMapper; +import info.frostfs.sdk.services.ContextAccessor; +import info.frostfs.sdk.services.NetmapClient; +import info.frostfs.sdk.tools.Verifier; import java.nio.charset.StandardCharsets; -import static info.FrostFS.sdk.tools.RequestConstructor.addMetaHeader; -import static info.FrostFS.sdk.tools.RequestSigner.sign; +import static info.frostfs.sdk.tools.RequestConstructor.addMetaHeader; +import static info.frostfs.sdk.tools.RequestSigner.sign; import static java.util.Objects.nonNull; public class NetmapService extends ContextAccessor implements NetmapClient { diff --git a/client/src/main/java/info/FrostFS/sdk/services/impl/ObjectReader.java b/client/src/main/java/info/frostfs/sdk/services/impl/ObjectReader.java similarity index 91% rename from client/src/main/java/info/FrostFS/sdk/services/impl/ObjectReader.java rename to client/src/main/java/info/frostfs/sdk/services/impl/ObjectReader.java index 116b3c2..62a9a70 100644 --- a/client/src/main/java/info/FrostFS/sdk/services/impl/ObjectReader.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/ObjectReader.java @@ -1,8 +1,8 @@ -package info.FrostFS.sdk.services.impl; +package info.frostfs.sdk.services.impl; -import frostFS.object.Service; -import frostFS.object.Types; -import info.FrostFS.sdk.tools.Verifier; +import frostfs.object.Service; +import frostfs.object.Types; +import info.frostfs.sdk.tools.Verifier; import java.util.Iterator; diff --git a/client/src/main/java/info/FrostFS/sdk/services/impl/ObjectService.java b/client/src/main/java/info/frostfs/sdk/services/impl/ObjectService.java similarity index 90% rename from client/src/main/java/info/FrostFS/sdk/services/impl/ObjectService.java rename to client/src/main/java/info/frostfs/sdk/services/impl/ObjectService.java index c9520e3..ca2652b 100644 --- a/client/src/main/java/info/FrostFS/sdk/services/impl/ObjectService.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/ObjectService.java @@ -1,26 +1,26 @@ -package info.FrostFS.sdk.services.impl; +package info.frostfs.sdk.services.impl; import com.google.common.collect.Iterables; import com.google.protobuf.ByteString; -import frostFS.object.ObjectServiceGrpc; -import frostFS.object.Service; -import frostFS.refs.Types; -import info.FrostFS.sdk.constants.AppConst; -import info.FrostFS.sdk.dto.Split; -import info.FrostFS.sdk.dto.container.ContainerId; -import info.FrostFS.sdk.dto.object.*; -import info.FrostFS.sdk.jdo.ClientEnvironment; -import info.FrostFS.sdk.jdo.PutObjectParameters; -import info.FrostFS.sdk.mappers.OwnerIdMapper; -import info.FrostFS.sdk.mappers.VersionMapper; -import info.FrostFS.sdk.mappers.container.ContainerIdMapper; -import info.FrostFS.sdk.mappers.object.ObjectFilterMapper; -import info.FrostFS.sdk.mappers.object.ObjectFrostFSMapper; -import info.FrostFS.sdk.mappers.object.ObjectHeaderMapper; -import info.FrostFS.sdk.mappers.object.ObjectIdMapper; -import info.FrostFS.sdk.services.ContextAccessor; -import info.FrostFS.sdk.services.ObjectClient; -import info.FrostFS.sdk.tools.Verifier; +import frostfs.object.ObjectServiceGrpc; +import frostfs.object.Service; +import frostfs.refs.Types; +import info.frostfs.sdk.constants.AppConst; +import info.frostfs.sdk.dto.Split; +import info.frostfs.sdk.dto.container.ContainerId; +import info.frostfs.sdk.dto.object.*; +import info.frostfs.sdk.jdo.ClientEnvironment; +import info.frostfs.sdk.jdo.PutObjectParameters; +import info.frostfs.sdk.mappers.OwnerIdMapper; +import info.frostfs.sdk.mappers.VersionMapper; +import info.frostfs.sdk.mappers.container.ContainerIdMapper; +import info.frostfs.sdk.mappers.object.ObjectFilterMapper; +import info.frostfs.sdk.mappers.object.ObjectFrostFSMapper; +import info.frostfs.sdk.mappers.object.ObjectHeaderMapper; +import info.frostfs.sdk.mappers.object.ObjectIdMapper; +import info.frostfs.sdk.services.ContextAccessor; +import info.frostfs.sdk.services.ObjectClient; +import info.frostfs.sdk.tools.Verifier; import org.apache.commons.collections4.CollectionUtils; import java.io.FileInputStream; @@ -29,10 +29,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import static info.FrostFS.sdk.Helper.getSha256; -import static info.FrostFS.sdk.tools.RequestConstructor.addMetaHeader; -import static info.FrostFS.sdk.tools.RequestConstructor.addObjectSessionToken; -import static info.FrostFS.sdk.tools.RequestSigner.sign; +import static info.frostfs.sdk.Helper.getSha256; +import static info.frostfs.sdk.tools.RequestConstructor.addMetaHeader; +import static info.frostfs.sdk.tools.RequestConstructor.addObjectSessionToken; +import static info.frostfs.sdk.tools.RequestSigner.sign; import static java.util.Objects.nonNull; public class ObjectService extends ContextAccessor implements ObjectClient { @@ -90,7 +90,7 @@ public class ObjectService extends ContextAccessor implements ObjectClient { addMetaHeader(request); addObjectSessionToken( request, sessionToken, ContainerIdMapper.toGrpcMessage(cid), ObjectIdMapper.toGrpcMessage(oid), - frostFS.session.Types.ObjectSessionContext.Verb.GET, getContext().getKey() + frostfs.session.Types.ObjectSessionContext.Verb.GET, getContext().getKey() ); sign(request, getContext().getKey()); @@ -159,7 +159,7 @@ public class ObjectService extends ContextAccessor implements ObjectClient { addMetaHeader(request); addObjectSessionToken( request, sessionToken, grpcObject.getHeader().getContainerId(), grpcObject.getObjectId(), - frostFS.session.Types.ObjectSessionContext.Verb.PUT, getContext().getKey() + frostfs.session.Types.ObjectSessionContext.Verb.PUT, getContext().getKey() ); sign(request, getContext().getKey()); @@ -170,7 +170,7 @@ public class ObjectService extends ContextAccessor implements ObjectClient { return ObjectId.fromHash(grpcObject.getObjectId().getValue().toByteArray()); } - private frostFS.object.Types.Object getObject(Service.GetRequest request) { + private frostfs.object.Types.Object getObject(Service.GetRequest request) { var iterator = getObjectInit(request); var obj = iterator.readHeader(); var payload = new byte[Math.toIntExact(obj.getHeader().getPayloadLength())]; @@ -218,7 +218,7 @@ public class ObjectService extends ContextAccessor implements ObjectClient { addMetaHeader(initRequest); addObjectSessionToken( initRequest, sessionToken, hdr.getContainerId(), oid, - frostFS.session.Types.ObjectSessionContext.Verb.PUT, getContext().getKey() + frostfs.session.Types.ObjectSessionContext.Verb.PUT, getContext().getKey() ); sign(initRequest, getContext().getKey()); diff --git a/client/src/main/java/info/FrostFS/sdk/services/impl/ObjectTools.java b/client/src/main/java/info/frostfs/sdk/services/impl/ObjectTools.java similarity index 75% rename from client/src/main/java/info/FrostFS/sdk/services/impl/ObjectTools.java rename to client/src/main/java/info/frostfs/sdk/services/impl/ObjectTools.java index 412fa66..5eb0680 100644 --- a/client/src/main/java/info/FrostFS/sdk/services/impl/ObjectTools.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/ObjectTools.java @@ -1,21 +1,21 @@ -package info.FrostFS.sdk.services.impl; +package info.frostfs.sdk.services.impl; import com.google.protobuf.ByteString; -import frostFS.object.Types; -import info.FrostFS.sdk.dto.object.ObjectFrostFS; -import info.FrostFS.sdk.dto.object.ObjectHeader; -import info.FrostFS.sdk.dto.object.ObjectId; -import info.FrostFS.sdk.jdo.ClientEnvironment; -import info.FrostFS.sdk.mappers.OwnerIdMapper; -import info.FrostFS.sdk.mappers.VersionMapper; -import info.FrostFS.sdk.mappers.object.ObjectHeaderMapper; -import info.FrostFS.sdk.mappers.object.ObjectIdMapper; -import info.FrostFS.sdk.services.ContextAccessor; -import info.FrostFS.sdk.services.ToolsClient; +import frostfs.object.Types; +import info.frostfs.sdk.dto.object.ObjectFrostFS; +import info.frostfs.sdk.dto.object.ObjectHeader; +import info.frostfs.sdk.dto.object.ObjectId; +import info.frostfs.sdk.jdo.ClientEnvironment; +import info.frostfs.sdk.mappers.OwnerIdMapper; +import info.frostfs.sdk.mappers.VersionMapper; +import info.frostfs.sdk.mappers.object.ObjectHeaderMapper; +import info.frostfs.sdk.mappers.object.ObjectIdMapper; +import info.frostfs.sdk.services.ContextAccessor; +import info.frostfs.sdk.services.ToolsClient; import org.apache.commons.collections4.ListUtils; -import static info.FrostFS.sdk.Helper.getSha256; -import static info.FrostFS.sdk.tools.RequestSigner.signData; +import static info.frostfs.sdk.Helper.getSha256; +import static info.frostfs.sdk.tools.RequestSigner.signData; import static java.util.Objects.nonNull; public class ObjectTools extends ContextAccessor implements ToolsClient { @@ -23,9 +23,9 @@ public class ObjectTools extends ContextAccessor implements ToolsClient { super(context); } - private static frostFS.refs.Types.Checksum sha256Checksum(byte[] data) { - return frostFS.refs.Types.Checksum.newBuilder() - .setType(frostFS.refs.Types.ChecksumType.SHA256) + private static frostfs.refs.Types.Checksum sha256Checksum(byte[] data) { + return frostfs.refs.Types.Checksum.newBuilder() + .setType(frostfs.refs.Types.ChecksumType.SHA256) .setSum(ByteString.copyFrom(getSha256(data))) .build(); } @@ -35,7 +35,7 @@ public class ObjectTools extends ContextAccessor implements ToolsClient { var grpcHeader = createHeader(header, new byte[]{}); return ObjectIdMapper.toModel( - frostFS.refs.Types.ObjectID.newBuilder().setValue(getSha256(grpcHeader)).build() + frostfs.refs.Types.ObjectID.newBuilder().setValue(getSha256(grpcHeader)).build() ); } @@ -58,13 +58,13 @@ public class ObjectTools extends ContextAccessor implements ToolsClient { if (nonNull(split.getParentHeader())) { var grpcParentHeader = createHeader(split.getParentHeader(), new byte[]{}); - var parent = frostFS.refs.Types.ObjectID.newBuilder().setValue(getSha256(grpcParentHeader)).build(); + var parent = frostfs.refs.Types.ObjectID.newBuilder().setValue(getSha256(grpcParentHeader)).build(); splitGrpc .setParent(parent) .setParentHeader(grpcParentHeader) .setParentSignature( - frostFS.refs.Types.Signature.newBuilder() + frostfs.refs.Types.Signature.newBuilder() .setKey(ByteString.copyFrom(getContext().getKey().getPublicKeyByte())) .setSign(ByteString.copyFrom(signData(getContext().getKey(), parent.toByteArray()))) ); @@ -78,13 +78,13 @@ public class ObjectTools extends ContextAccessor implements ToolsClient { } var grpcHeader = grpcHeaderBuilder.build(); - var objectId = frostFS.refs.Types.ObjectID.newBuilder().setValue(getSha256(grpcHeader)).build(); + var objectId = frostfs.refs.Types.ObjectID.newBuilder().setValue(getSha256(grpcHeader)).build(); return Types.Object.newBuilder() .setHeader(grpcHeader) .setObjectId(objectId) .setPayload(ByteString.copyFrom(objectFrostFs.getPayload())) .setSignature( - frostFS.refs.Types.Signature.newBuilder() + frostfs.refs.Types.Signature.newBuilder() .setKey(ByteString.copyFrom(getContext().getKey().getPublicKeyByte())) .setSign(ByteString.copyFrom(signData(getContext().getKey(), objectId.toByteArray()))) ) diff --git a/client/src/main/java/info/FrostFS/sdk/services/impl/ObjectWriter.java b/client/src/main/java/info/frostfs/sdk/services/impl/ObjectWriter.java similarity index 93% rename from client/src/main/java/info/FrostFS/sdk/services/impl/ObjectWriter.java rename to client/src/main/java/info/frostfs/sdk/services/impl/ObjectWriter.java index 01ae85e..8642dc8 100644 --- a/client/src/main/java/info/FrostFS/sdk/services/impl/ObjectWriter.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/ObjectWriter.java @@ -1,7 +1,7 @@ -package info.FrostFS.sdk.services.impl; +package info.frostfs.sdk.services.impl; -import frostFS.object.ObjectServiceGrpc; -import frostFS.object.Service; +import frostfs.object.ObjectServiceGrpc; +import frostfs.object.Service; import io.grpc.stub.StreamObserver; import static java.util.Objects.isNull; diff --git a/client/src/main/java/info/FrostFS/sdk/services/impl/SearchReader.java b/client/src/main/java/info/frostfs/sdk/services/impl/SearchReader.java similarity index 72% rename from client/src/main/java/info/FrostFS/sdk/services/impl/SearchReader.java rename to client/src/main/java/info/frostfs/sdk/services/impl/SearchReader.java index 864d6e5..873d48b 100644 --- a/client/src/main/java/info/FrostFS/sdk/services/impl/SearchReader.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/SearchReader.java @@ -1,7 +1,7 @@ -package info.FrostFS.sdk.services.impl; +package info.frostfs.sdk.services.impl; -import frostFS.object.Service; -import info.FrostFS.sdk.tools.Verifier; +import frostfs.object.Service; +import info.frostfs.sdk.tools.Verifier; import java.util.Iterator; import java.util.List; @@ -13,7 +13,7 @@ public class SearchReader { this.call = call; } - public List read() { + public List read() { if (!call.hasNext()) { return null; } diff --git a/client/src/main/java/info/FrostFS/sdk/services/impl/SessionService.java b/client/src/main/java/info/frostfs/sdk/services/impl/SessionService.java similarity index 75% rename from client/src/main/java/info/FrostFS/sdk/services/impl/SessionService.java rename to client/src/main/java/info/frostfs/sdk/services/impl/SessionService.java index 35e5967..d6bf950 100644 --- a/client/src/main/java/info/FrostFS/sdk/services/impl/SessionService.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/SessionService.java @@ -1,17 +1,18 @@ -package info.FrostFS.sdk.services.impl; +package info.frostfs.sdk.services.impl; -import frostFS.session.Service; -import frostFS.session.SessionServiceGrpc; -import frostFS.session.Types; -import info.FrostFS.sdk.dto.SessionToken; -import info.FrostFS.sdk.jdo.ClientEnvironment; -import info.FrostFS.sdk.mappers.OwnerIdMapper; -import info.FrostFS.sdk.mappers.SessionMapper; -import info.FrostFS.sdk.services.ContextAccessor; -import info.FrostFS.sdk.services.SessionClient; +import frostfs.session.Service; +import frostfs.session.SessionServiceGrpc; +import frostfs.session.Types; +import info.frostfs.sdk.dto.SessionToken; +import info.frostfs.sdk.jdo.ClientEnvironment; +import info.frostfs.sdk.mappers.OwnerIdMapper; +import info.frostfs.sdk.mappers.SessionMapper; +import info.frostfs.sdk.services.ContextAccessor; +import info.frostfs.sdk.services.SessionClient; +import info.frostfs.sdk.tools.RequestConstructor; -import static info.FrostFS.sdk.tools.RequestConstructor.addMetaHeader; -import static info.FrostFS.sdk.tools.RequestSigner.sign; +import static info.frostfs.sdk.tools.RequestConstructor.addMetaHeader; +import static info.frostfs.sdk.tools.RequestSigner.sign; public class SessionService extends ContextAccessor implements SessionClient { private final SessionServiceGrpc.SessionServiceBlockingStub serviceBlockingStub; @@ -36,7 +37,7 @@ public class SessionService extends ContextAccessor implements SessionClient { .setExpiration(expiration).build() ); - addMetaHeader(request); + RequestConstructor.addMetaHeader(request); sign(request, getContext().getKey()); return createSession(request.build()); diff --git a/client/src/main/java/info/FrostFS/sdk/tools/GrpcClient.java b/client/src/main/java/info/frostfs/sdk/tools/GrpcClient.java similarity index 96% rename from client/src/main/java/info/FrostFS/sdk/tools/GrpcClient.java rename to client/src/main/java/info/frostfs/sdk/tools/GrpcClient.java index 61cfee2..729d3e5 100644 --- a/client/src/main/java/info/FrostFS/sdk/tools/GrpcClient.java +++ b/client/src/main/java/info/frostfs/sdk/tools/GrpcClient.java @@ -1,4 +1,4 @@ -package info.FrostFS.sdk.tools; +package info.frostfs.sdk.tools; import io.grpc.Channel; import io.grpc.ChannelCredentials; diff --git a/client/src/main/java/info/FrostFS/sdk/tools/MessageHelper.java b/client/src/main/java/info/frostfs/sdk/tools/MessageHelper.java similarity index 94% rename from client/src/main/java/info/FrostFS/sdk/tools/MessageHelper.java rename to client/src/main/java/info/frostfs/sdk/tools/MessageHelper.java index 4e11b06..00f87c4 100644 --- a/client/src/main/java/info/FrostFS/sdk/tools/MessageHelper.java +++ b/client/src/main/java/info/frostfs/sdk/tools/MessageHelper.java @@ -1,4 +1,4 @@ -package info.FrostFS.sdk.tools; +package info.frostfs.sdk.tools; import com.google.protobuf.Message; import com.google.protobuf.MessageOrBuilder; diff --git a/client/src/main/java/info/FrostFS/sdk/tools/RequestConstructor.java b/client/src/main/java/info/frostfs/sdk/tools/RequestConstructor.java similarity index 74% rename from client/src/main/java/info/FrostFS/sdk/tools/RequestConstructor.java rename to client/src/main/java/info/frostfs/sdk/tools/RequestConstructor.java index 3d8d02a..9660de5 100644 --- a/client/src/main/java/info/FrostFS/sdk/tools/RequestConstructor.java +++ b/client/src/main/java/info/frostfs/sdk/tools/RequestConstructor.java @@ -1,15 +1,15 @@ -package info.FrostFS.sdk.tools; +package info.frostfs.sdk.tools; import com.google.protobuf.Message; -import frostFS.session.Types; -import info.FrostFS.sdk.dto.MetaHeader; -import info.FrostFS.sdk.jdo.ECDsa; -import info.FrostFS.sdk.mappers.MetaHeaderMapper; +import frostfs.session.Types; +import info.frostfs.sdk.dto.MetaHeader; +import info.frostfs.sdk.jdo.ECDsa; +import info.frostfs.sdk.mappers.MetaHeaderMapper; -import static info.FrostFS.sdk.constants.FieldConst.META_HEADER_FIELD_NAME; -import static info.FrostFS.sdk.tools.MessageHelper.getField; -import static info.FrostFS.sdk.tools.MessageHelper.setField; -import static info.FrostFS.sdk.tools.RequestSigner.signMessagePart; +import static info.frostfs.sdk.constants.FieldConst.META_HEADER_FIELD_NAME; +import static info.frostfs.sdk.tools.MessageHelper.getField; +import static info.frostfs.sdk.tools.MessageHelper.setField; +import static info.frostfs.sdk.tools.RequestSigner.signMessagePart; import static java.util.Objects.isNull; public class RequestConstructor { @@ -27,8 +27,8 @@ public class RequestConstructor { public static void addObjectSessionToken(Message.Builder request, Types.SessionToken sessionToken, - frostFS.refs.Types.ContainerID cid, - frostFS.refs.Types.ObjectID oid, + frostfs.refs.Types.ContainerID cid, + frostfs.refs.Types.ObjectID oid, Types.ObjectSessionContext.Verb verb, ECDsa key) { var header = (Types.RequestMetaHeader) getField(request, META_HEADER_FIELD_NAME); diff --git a/client/src/main/java/info/FrostFS/sdk/tools/RequestSigner.java b/client/src/main/java/info/frostfs/sdk/tools/RequestSigner.java similarity index 72% rename from client/src/main/java/info/FrostFS/sdk/tools/RequestSigner.java rename to client/src/main/java/info/frostfs/sdk/tools/RequestSigner.java index daca2cc..315af58 100644 --- a/client/src/main/java/info/FrostFS/sdk/tools/RequestSigner.java +++ b/client/src/main/java/info/frostfs/sdk/tools/RequestSigner.java @@ -1,9 +1,10 @@ -package info.FrostFS.sdk.tools; +package info.frostfs.sdk.tools; import com.google.protobuf.ByteString; import com.google.protobuf.Message; -import frostFS.session.Types; -import info.FrostFS.sdk.jdo.ECDsa; +import frostfs.session.Types; +import info.frostfs.sdk.jdo.ECDsa; +import info.frostfs.sdk.constants.CryptoConst; import org.apache.commons.codec.digest.DigestUtils; import org.bouncycastle.asn1.sec.SECNamedCurves; import org.bouncycastle.asn1.sec.SECObjectIdentifiers; @@ -15,10 +16,7 @@ import org.bouncycastle.crypto.signers.HMacDSAKCalculator; import java.math.BigInteger; import java.security.Signature; -import static info.FrostFS.sdk.constants.CryptoConst.SIGNATURE_ALGORITHM; -import static info.FrostFS.sdk.constants.FieldConst.*; -import static info.FrostFS.sdk.tools.MessageHelper.getField; -import static info.FrostFS.sdk.tools.MessageHelper.setField; +import static info.frostfs.sdk.constants.FieldConst.*; import static org.bouncycastle.crypto.util.DigestFactory.createSHA256; import static org.bouncycastle.util.BigIntegers.asUnsignedByteArray; @@ -30,7 +28,7 @@ public class RequestSigner { var hash = new byte[65]; hash[0] = 0x04; try { - Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); + Signature signature = Signature.getInstance(CryptoConst.SIGNATURE_ALGORITHM); signature.initSign(key.getPrivateKey()); signature.update(DigestUtils.sha512(data)); byte[] sig = signature.sign(); @@ -67,33 +65,33 @@ public class RequestSigner { return signature; } - public static frostFS.refs.Types.SignatureRFC6979 signRFC6979(ECDsa key, Message message) { - return frostFS.refs.Types.SignatureRFC6979.newBuilder() + public static frostfs.refs.Types.SignatureRFC6979 signRFC6979(ECDsa key, Message message) { + return frostfs.refs.Types.SignatureRFC6979.newBuilder() .setKey(ByteString.copyFrom(key.getPublicKeyByte())) .setSign(ByteString.copyFrom(signRFC6979(key, message.toByteArray()))) .build(); } - public static frostFS.refs.Types.SignatureRFC6979 signRFC6979(ECDsa key, ByteString data) { - return frostFS.refs.Types.SignatureRFC6979.newBuilder() + public static frostfs.refs.Types.SignatureRFC6979 signRFC6979(ECDsa key, ByteString data) { + return frostfs.refs.Types.SignatureRFC6979.newBuilder() .setKey(ByteString.copyFrom(key.getPublicKeyByte())) .setSign(ByteString.copyFrom(signRFC6979(key, data.toByteArray()))) .build(); } - public static frostFS.refs.Types.Signature signMessagePart(ECDsa key, Message data) { + public static frostfs.refs.Types.Signature signMessagePart(ECDsa key, Message data) { var data2Sign = data.getSerializedSize() == 0 ? new byte[]{} : data.toByteArray(); - return frostFS.refs.Types.Signature.newBuilder() + return frostfs.refs.Types.Signature.newBuilder() .setKey(ByteString.copyFrom(key.getPublicKeyByte())) .setSign(ByteString.copyFrom(signData(key, data2Sign))) .build(); } public static void sign(Message.Builder request, ECDsa key) { - var meta = getField(request, META_HEADER_FIELD_NAME); - var body = getField(request, BODY_FIELD_NAME); - var verify = getField(request, VERIFY_HEADER_FIELD_NAME); - var verifyOrigin = getField(verify, ORIGIN_FIELD_NAME); + var meta = MessageHelper.getField(request, META_HEADER_FIELD_NAME); + var body = MessageHelper.getField(request, BODY_FIELD_NAME); + var verify = MessageHelper.getField(request, VERIFY_HEADER_FIELD_NAME); + var verifyOrigin = MessageHelper.getField(verify, ORIGIN_FIELD_NAME); Message.Builder verifyBuilder; if (verify instanceof Types.RequestVerificationHeader) { @@ -107,14 +105,14 @@ public class RequestSigner { } if (verifyOrigin.getSerializedSize() == 0) { - setField(verifyBuilder, BODY_SIGNATURE_FIELD_NAME, signMessagePart(key, body)); + MessageHelper.setField(verifyBuilder, BODY_SIGNATURE_FIELD_NAME, signMessagePart(key, body)); } else { - setField(verifyBuilder, ORIGIN_FIELD_NAME, verifyOrigin); + MessageHelper.setField(verifyBuilder, ORIGIN_FIELD_NAME, verifyOrigin); } - setField(verifyBuilder, META_SIGNATURE_FIELD_NAME, signMessagePart(key, meta)); - setField(verifyBuilder, ORIGIN_SIGNATURE_FIELD_NAME, signMessagePart(key, verifyOrigin)); - setField(request, VERIFY_HEADER_FIELD_NAME, verifyBuilder.build()); + MessageHelper.setField(verifyBuilder, META_SIGNATURE_FIELD_NAME, signMessagePart(key, meta)); + MessageHelper.setField(verifyBuilder, ORIGIN_SIGNATURE_FIELD_NAME, signMessagePart(key, verifyOrigin)); + MessageHelper.setField(request, VERIFY_HEADER_FIELD_NAME, verifyBuilder.build()); } } diff --git a/client/src/main/java/info/FrostFS/sdk/tools/Verifier.java b/client/src/main/java/info/frostfs/sdk/tools/Verifier.java similarity index 80% rename from client/src/main/java/info/FrostFS/sdk/tools/Verifier.java rename to client/src/main/java/info/frostfs/sdk/tools/Verifier.java index 14e7327..8fed127 100644 --- a/client/src/main/java/info/FrostFS/sdk/tools/Verifier.java +++ b/client/src/main/java/info/frostfs/sdk/tools/Verifier.java @@ -1,8 +1,9 @@ -package info.FrostFS.sdk.tools; +package info.frostfs.sdk.tools; import com.google.protobuf.Message; -import frostFS.session.Types; -import info.FrostFS.sdk.mappers.StatusMapper; +import frostfs.session.Types; +import info.frostfs.sdk.mappers.StatusMapper; +import info.frostfs.sdk.constants.CryptoConst; import org.apache.commons.codec.digest.DigestUtils; import org.bouncycastle.asn1.sec.SECNamedCurves; import org.bouncycastle.asn1.sec.SECObjectIdentifiers; @@ -16,10 +17,8 @@ import java.security.PublicKey; import java.security.Signature; import java.util.Arrays; -import static info.FrostFS.sdk.KeyExtension.getPublicKeyFromBytes; -import static info.FrostFS.sdk.constants.CryptoConst.SIGNATURE_ALGORITHM; -import static info.FrostFS.sdk.constants.FieldConst.*; -import static info.FrostFS.sdk.tools.MessageHelper.getField; +import static info.frostfs.sdk.KeyExtension.getPublicKeyFromBytes; +import static info.frostfs.sdk.constants.FieldConst.*; import static java.util.Objects.isNull; import static org.bouncycastle.crypto.util.DigestFactory.createSHA256; import static org.bouncycastle.util.BigIntegers.fromUnsignedByteArray; @@ -29,7 +28,7 @@ public class Verifier { public static final String ERROR_INVALID_RESPONSE = "Invalid response"; public static final int RFC6979_SIG_SIZE = 64; - public static boolean verifyRFC6979(frostFS.refs.Types.SignatureRFC6979 signature, Message data) { + public static boolean verifyRFC6979(frostfs.refs.Types.SignatureRFC6979 signature, Message data) { return verifyRFC6979(signature.getKey().toByteArray(), data.toByteArray(), signature.getSign().toByteArray()); } @@ -68,7 +67,7 @@ public class Verifier { throw new IllegalArgumentException(ERROR_INVALID_RESPONSE); } - var metaHeader = (Types.ResponseMetaHeader) getField(response, META_HEADER_FIELD_NAME); + var metaHeader = (Types.ResponseMetaHeader) MessageHelper.getField(response, META_HEADER_FIELD_NAME); var status = StatusMapper.toModel(metaHeader.getStatus()); if (!status.isSuccess()) { throw new IllegalArgumentException(status.toString()); @@ -76,16 +75,16 @@ public class Verifier { } public static boolean verify(Message response) { - var body = getField(response, BODY_FIELD_NAME); - var metaHeader = (Types.ResponseMetaHeader) getField(response, META_HEADER_FIELD_NAME); - var verifyHeader = (Types.ResponseVerificationHeader) getField(response, VERIFY_HEADER_FIELD_NAME); + var body = MessageHelper.getField(response, BODY_FIELD_NAME); + var metaHeader = (Types.ResponseMetaHeader) MessageHelper.getField(response, META_HEADER_FIELD_NAME); + var verifyHeader = (Types.ResponseVerificationHeader) MessageHelper.getField(response, VERIFY_HEADER_FIELD_NAME); return verifyMatryoshkaLevel(body, metaHeader, verifyHeader); } public static boolean verifyMatryoshkaLevel(Message data, - frostFS.session.Types.ResponseMetaHeader meta, - frostFS.session.Types.ResponseVerificationHeader verification) { + frostfs.session.Types.ResponseMetaHeader meta, + frostfs.session.Types.ResponseVerificationHeader verification) { if (!verifyMessagePart(verification.getMetaSignature(), meta)) return false; var origin = verification.getOrigin(); if (!verifyMessagePart(verification.getOriginSignature(), origin)) return false; @@ -96,7 +95,7 @@ public class Verifier { && verifyMatryoshkaLevel(data, meta.getOrigin(), origin); } - public static boolean verifyMessagePart(frostFS.refs.Types.Signature sig, Message data) { + public static boolean verifyMessagePart(frostfs.refs.Types.Signature sig, Message data) { if (sig.getSerializedSize() == 0 || sig.getKey().isEmpty() || sig.getSign().isEmpty()) return false; var publicKey = getPublicKeyFromBytes(sig.getKey().toByteArray()); @@ -107,7 +106,7 @@ public class Verifier { public static boolean verifyData(PublicKey publicKey, byte[] data, byte[] sig) { try { - Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); + Signature signature = Signature.getInstance(CryptoConst.SIGNATURE_ALGORITHM); signature.initVerify(publicKey); signature.update(DigestUtils.sha512(data)); return signature.verify(Arrays.copyOfRange(sig, 1, sig.length)); diff --git a/cryptography/pom.xml b/cryptography/pom.xml index f92accd..156b0bd 100644 --- a/cryptography/pom.xml +++ b/cryptography/pom.xml @@ -4,8 +4,8 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - info.FrostFS.sdk - FrostFS-sdk-java + info.frostfs.sdk + frostfs-sdk-java 0.1.0 diff --git a/cryptography/src/main/java/info/FrostFS/sdk/ArrayHelper.java b/cryptography/src/main/java/info/frostfs/sdk/ArrayHelper.java similarity index 93% rename from cryptography/src/main/java/info/FrostFS/sdk/ArrayHelper.java rename to cryptography/src/main/java/info/frostfs/sdk/ArrayHelper.java index 5274f60..ae43afd 100644 --- a/cryptography/src/main/java/info/FrostFS/sdk/ArrayHelper.java +++ b/cryptography/src/main/java/info/frostfs/sdk/ArrayHelper.java @@ -1,4 +1,4 @@ -package info.FrostFS.sdk; +package info.frostfs.sdk; public class ArrayHelper { diff --git a/cryptography/src/main/java/info/FrostFS/sdk/Base58.java b/cryptography/src/main/java/info/frostfs/sdk/Base58.java similarity index 97% rename from cryptography/src/main/java/info/FrostFS/sdk/Base58.java rename to cryptography/src/main/java/info/frostfs/sdk/Base58.java index 9fe27af..f75d705 100644 --- a/cryptography/src/main/java/info/FrostFS/sdk/Base58.java +++ b/cryptography/src/main/java/info/frostfs/sdk/Base58.java @@ -1,9 +1,9 @@ -package info.FrostFS.sdk; +package info.frostfs.sdk; import java.util.Arrays; -import static info.FrostFS.sdk.ArrayHelper.concat; -import static info.FrostFS.sdk.Helper.getSha256; +import static info.frostfs.sdk.ArrayHelper.concat; +import static info.frostfs.sdk.Helper.getSha256; import static java.util.Objects.isNull; public class Base58 { diff --git a/cryptography/src/main/java/info/FrostFS/sdk/Helper.java b/cryptography/src/main/java/info/frostfs/sdk/Helper.java similarity index 97% rename from cryptography/src/main/java/info/FrostFS/sdk/Helper.java rename to cryptography/src/main/java/info/frostfs/sdk/Helper.java index 083121b..a6068e2 100644 --- a/cryptography/src/main/java/info/FrostFS/sdk/Helper.java +++ b/cryptography/src/main/java/info/frostfs/sdk/Helper.java @@ -1,4 +1,4 @@ -package info.FrostFS.sdk; +package info.frostfs.sdk; import com.google.protobuf.ByteString; import com.google.protobuf.Message; diff --git a/cryptography/src/main/java/info/FrostFS/sdk/KeyExtension.java b/cryptography/src/main/java/info/frostfs/sdk/KeyExtension.java similarity index 98% rename from cryptography/src/main/java/info/FrostFS/sdk/KeyExtension.java rename to cryptography/src/main/java/info/frostfs/sdk/KeyExtension.java index d057a04..4ef8ffe 100644 --- a/cryptography/src/main/java/info/FrostFS/sdk/KeyExtension.java +++ b/cryptography/src/main/java/info/frostfs/sdk/KeyExtension.java @@ -1,4 +1,4 @@ -package info.FrostFS.sdk; +package info.frostfs.sdk; import org.bouncycastle.asn1.sec.SECNamedCurves; import org.bouncycastle.asn1.sec.SECObjectIdentifiers; @@ -23,8 +23,8 @@ import java.security.spec.ECPublicKeySpec; import java.security.spec.InvalidKeySpecException; import java.util.Arrays; -import static info.FrostFS.sdk.Helper.getRIPEMD160; -import static info.FrostFS.sdk.Helper.getSha256; +import static info.frostfs.sdk.Helper.getRIPEMD160; +import static info.frostfs.sdk.Helper.getSha256; import static org.bouncycastle.util.BigIntegers.fromUnsignedByteArray; public class KeyExtension { diff --git a/models/pom.xml b/models/pom.xml index 1f04f5e..e9dd655 100644 --- a/models/pom.xml +++ b/models/pom.xml @@ -4,8 +4,8 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - info.FrostFS.sdk - FrostFS-sdk-java + info.frostfs.sdk + frostfs-sdk-java 0.1.0 @@ -24,12 +24,12 @@ 3.14.0 - info.FrostFS.sdk + info.frostfs.sdk cryptography 0.1.0 - info.FrostFS.sdk + info.frostfs.sdk protos 0.1.0 diff --git a/models/src/main/java/info/FrostFS/sdk/UUIDExtension.java b/models/src/main/java/info/frostfs/sdk/UUIDExtension.java similarity index 95% rename from models/src/main/java/info/FrostFS/sdk/UUIDExtension.java rename to models/src/main/java/info/frostfs/sdk/UUIDExtension.java index df146ec..7edc01c 100644 --- a/models/src/main/java/info/FrostFS/sdk/UUIDExtension.java +++ b/models/src/main/java/info/frostfs/sdk/UUIDExtension.java @@ -1,4 +1,4 @@ -package info.FrostFS.sdk; +package info.frostfs.sdk; import java.nio.ByteBuffer; import java.util.UUID; diff --git a/models/src/main/java/info/FrostFS/sdk/constants/AppConst.java b/models/src/main/java/info/frostfs/sdk/constants/AppConst.java similarity index 79% rename from models/src/main/java/info/FrostFS/sdk/constants/AppConst.java rename to models/src/main/java/info/frostfs/sdk/constants/AppConst.java index 8880eb0..ea60f0e 100644 --- a/models/src/main/java/info/FrostFS/sdk/constants/AppConst.java +++ b/models/src/main/java/info/frostfs/sdk/constants/AppConst.java @@ -1,4 +1,4 @@ -package info.FrostFS.sdk.constants; +package info.frostfs.sdk.constants; public class AppConst { public static final int OBJECT_CHUNK_SIZE = 3 * (1 << 20); diff --git a/models/src/main/java/info/FrostFS/sdk/constants/FieldConst.java b/models/src/main/java/info/frostfs/sdk/constants/FieldConst.java similarity index 93% rename from models/src/main/java/info/FrostFS/sdk/constants/FieldConst.java rename to models/src/main/java/info/frostfs/sdk/constants/FieldConst.java index 761258a..fe85aad 100644 --- a/models/src/main/java/info/FrostFS/sdk/constants/FieldConst.java +++ b/models/src/main/java/info/frostfs/sdk/constants/FieldConst.java @@ -1,4 +1,4 @@ -package info.FrostFS.sdk.constants; +package info.frostfs.sdk.constants; public class FieldConst { public static final String META_HEADER_FIELD_NAME = "meta_header"; diff --git a/models/src/main/java/info/FrostFS/sdk/constants/XHeaderConst.java b/models/src/main/java/info/frostfs/sdk/constants/XHeaderConst.java similarity index 89% rename from models/src/main/java/info/FrostFS/sdk/constants/XHeaderConst.java rename to models/src/main/java/info/frostfs/sdk/constants/XHeaderConst.java index 8c435e7..c1cc83c 100644 --- a/models/src/main/java/info/FrostFS/sdk/constants/XHeaderConst.java +++ b/models/src/main/java/info/frostfs/sdk/constants/XHeaderConst.java @@ -1,4 +1,4 @@ -package info.FrostFS.sdk.constants; +package info.frostfs.sdk.constants; public class XHeaderConst { public static final String RESERVED_XHEADER_PREFIX = "__SYSTEM__"; diff --git a/models/src/main/java/info/FrostFS/sdk/dto/MetaHeader.java b/models/src/main/java/info/frostfs/sdk/dto/MetaHeader.java similarity index 96% rename from models/src/main/java/info/FrostFS/sdk/dto/MetaHeader.java rename to models/src/main/java/info/frostfs/sdk/dto/MetaHeader.java index b0eead0..4e22f46 100644 --- a/models/src/main/java/info/FrostFS/sdk/dto/MetaHeader.java +++ b/models/src/main/java/info/frostfs/sdk/dto/MetaHeader.java @@ -1,4 +1,4 @@ -package info.FrostFS.sdk.dto; +package info.frostfs.sdk.dto; public class MetaHeader { private Version version; diff --git a/models/src/main/java/info/FrostFS/sdk/dto/OwnerId.java b/models/src/main/java/info/frostfs/sdk/dto/OwnerId.java similarity index 75% rename from models/src/main/java/info/FrostFS/sdk/dto/OwnerId.java rename to models/src/main/java/info/frostfs/sdk/dto/OwnerId.java index 40b4f04..d4a173b 100644 --- a/models/src/main/java/info/FrostFS/sdk/dto/OwnerId.java +++ b/models/src/main/java/info/frostfs/sdk/dto/OwnerId.java @@ -1,8 +1,8 @@ -package info.FrostFS.sdk.dto; +package info.frostfs.sdk.dto; -import info.FrostFS.sdk.Base58; +import info.frostfs.sdk.Base58; -import static info.FrostFS.sdk.KeyExtension.publicKeyToAddress; +import static info.frostfs.sdk.KeyExtension.publicKeyToAddress; public class OwnerId { private final String value; diff --git a/models/src/main/java/info/FrostFS/sdk/dto/SessionToken.java b/models/src/main/java/info/frostfs/sdk/dto/SessionToken.java similarity index 91% rename from models/src/main/java/info/FrostFS/sdk/dto/SessionToken.java rename to models/src/main/java/info/frostfs/sdk/dto/SessionToken.java index 5980875..2f4055b 100644 --- a/models/src/main/java/info/FrostFS/sdk/dto/SessionToken.java +++ b/models/src/main/java/info/frostfs/sdk/dto/SessionToken.java @@ -1,4 +1,4 @@ -package info.FrostFS.sdk.dto; +package info.frostfs.sdk.dto; public class SessionToken { private final byte[] id; diff --git a/models/src/main/java/info/FrostFS/sdk/dto/Signature.java b/models/src/main/java/info/frostfs/sdk/dto/Signature.java similarity index 87% rename from models/src/main/java/info/FrostFS/sdk/dto/Signature.java rename to models/src/main/java/info/frostfs/sdk/dto/Signature.java index 0ab5b3e..5b2b85d 100644 --- a/models/src/main/java/info/FrostFS/sdk/dto/Signature.java +++ b/models/src/main/java/info/frostfs/sdk/dto/Signature.java @@ -1,6 +1,6 @@ -package info.FrostFS.sdk.dto; +package info.frostfs.sdk.dto; -import info.FrostFS.sdk.enums.SignatureScheme; +import info.frostfs.sdk.enums.SignatureScheme; public class Signature { private byte[] key; diff --git a/models/src/main/java/info/FrostFS/sdk/dto/Split.java b/models/src/main/java/info/frostfs/sdk/dto/Split.java similarity index 91% rename from models/src/main/java/info/FrostFS/sdk/dto/Split.java rename to models/src/main/java/info/frostfs/sdk/dto/Split.java index be51331..2b5726a 100644 --- a/models/src/main/java/info/FrostFS/sdk/dto/Split.java +++ b/models/src/main/java/info/frostfs/sdk/dto/Split.java @@ -1,7 +1,7 @@ -package info.FrostFS.sdk.dto; +package info.frostfs.sdk.dto; -import info.FrostFS.sdk.dto.object.ObjectHeader; -import info.FrostFS.sdk.dto.object.ObjectId; +import info.frostfs.sdk.dto.object.ObjectHeader; +import info.frostfs.sdk.dto.object.ObjectId; import java.util.ArrayList; import java.util.List; diff --git a/models/src/main/java/info/FrostFS/sdk/dto/SplitId.java b/models/src/main/java/info/frostfs/sdk/dto/SplitId.java similarity index 85% rename from models/src/main/java/info/FrostFS/sdk/dto/SplitId.java rename to models/src/main/java/info/frostfs/sdk/dto/SplitId.java index b01b8d0..6ecc031 100644 --- a/models/src/main/java/info/FrostFS/sdk/dto/SplitId.java +++ b/models/src/main/java/info/frostfs/sdk/dto/SplitId.java @@ -1,9 +1,9 @@ -package info.FrostFS.sdk.dto; +package info.frostfs.sdk.dto; import java.util.UUID; -import static info.FrostFS.sdk.UUIDExtension.asBytes; -import static info.FrostFS.sdk.UUIDExtension.asUuid; +import static info.frostfs.sdk.UUIDExtension.asBytes; +import static info.frostfs.sdk.UUIDExtension.asUuid; import static java.util.Objects.isNull; public class SplitId { diff --git a/models/src/main/java/info/FrostFS/sdk/dto/Status.java b/models/src/main/java/info/frostfs/sdk/dto/Status.java similarity index 92% rename from models/src/main/java/info/FrostFS/sdk/dto/Status.java rename to models/src/main/java/info/frostfs/sdk/dto/Status.java index d766546..dd242a3 100644 --- a/models/src/main/java/info/FrostFS/sdk/dto/Status.java +++ b/models/src/main/java/info/frostfs/sdk/dto/Status.java @@ -1,6 +1,6 @@ -package info.FrostFS.sdk.dto; +package info.frostfs.sdk.dto; -import info.FrostFS.sdk.enums.StatusCode; +import info.frostfs.sdk.enums.StatusCode; import static java.util.Objects.isNull; diff --git a/models/src/main/java/info/FrostFS/sdk/dto/Version.java b/models/src/main/java/info/frostfs/sdk/dto/Version.java similarity index 95% rename from models/src/main/java/info/FrostFS/sdk/dto/Version.java rename to models/src/main/java/info/frostfs/sdk/dto/Version.java index c498b49..563d8b6 100644 --- a/models/src/main/java/info/FrostFS/sdk/dto/Version.java +++ b/models/src/main/java/info/frostfs/sdk/dto/Version.java @@ -1,4 +1,4 @@ -package info.FrostFS.sdk.dto; +package info.frostfs.sdk.dto; public class Version { private int major; diff --git a/models/src/main/java/info/FrostFS/sdk/dto/container/Container.java b/models/src/main/java/info/frostfs/sdk/dto/container/Container.java similarity index 86% rename from models/src/main/java/info/FrostFS/sdk/dto/container/Container.java rename to models/src/main/java/info/frostfs/sdk/dto/container/Container.java index b952fcf..e07c28a 100644 --- a/models/src/main/java/info/FrostFS/sdk/dto/container/Container.java +++ b/models/src/main/java/info/frostfs/sdk/dto/container/Container.java @@ -1,8 +1,8 @@ -package info.FrostFS.sdk.dto.container; +package info.frostfs.sdk.dto.container; -import info.FrostFS.sdk.dto.Version; -import info.FrostFS.sdk.dto.netmap.PlacementPolicy; -import info.FrostFS.sdk.enums.BasicAcl; +import info.frostfs.sdk.dto.Version; +import info.frostfs.sdk.dto.netmap.PlacementPolicy; +import info.frostfs.sdk.enums.BasicAcl; import java.util.UUID; diff --git a/models/src/main/java/info/FrostFS/sdk/dto/container/ContainerId.java b/models/src/main/java/info/frostfs/sdk/dto/container/ContainerId.java similarity index 85% rename from models/src/main/java/info/FrostFS/sdk/dto/container/ContainerId.java rename to models/src/main/java/info/frostfs/sdk/dto/container/ContainerId.java index fd8d529..b24a069 100644 --- a/models/src/main/java/info/FrostFS/sdk/dto/container/ContainerId.java +++ b/models/src/main/java/info/frostfs/sdk/dto/container/ContainerId.java @@ -1,7 +1,7 @@ -package info.FrostFS.sdk.dto.container; +package info.frostfs.sdk.dto.container; -import info.FrostFS.sdk.Base58; -import info.FrostFS.sdk.constants.AppConst; +import info.frostfs.sdk.Base58; +import info.frostfs.sdk.constants.AppConst; public class ContainerId { private String value; diff --git a/models/src/main/java/info/FrostFS/sdk/dto/netmap/NetmapSnapshot.java b/models/src/main/java/info/frostfs/sdk/dto/netmap/NetmapSnapshot.java similarity index 92% rename from models/src/main/java/info/FrostFS/sdk/dto/netmap/NetmapSnapshot.java rename to models/src/main/java/info/frostfs/sdk/dto/netmap/NetmapSnapshot.java index 8619f00..9c2ef2c 100644 --- a/models/src/main/java/info/FrostFS/sdk/dto/netmap/NetmapSnapshot.java +++ b/models/src/main/java/info/frostfs/sdk/dto/netmap/NetmapSnapshot.java @@ -1,4 +1,4 @@ -package info.FrostFS.sdk.dto.netmap; +package info.frostfs.sdk.dto.netmap; import java.util.List; diff --git a/models/src/main/java/info/FrostFS/sdk/dto/netmap/NodeInfo.java b/models/src/main/java/info/frostfs/sdk/dto/netmap/NodeInfo.java similarity index 89% rename from models/src/main/java/info/FrostFS/sdk/dto/netmap/NodeInfo.java rename to models/src/main/java/info/frostfs/sdk/dto/netmap/NodeInfo.java index bd78146..2be1b1b 100644 --- a/models/src/main/java/info/FrostFS/sdk/dto/netmap/NodeInfo.java +++ b/models/src/main/java/info/frostfs/sdk/dto/netmap/NodeInfo.java @@ -1,7 +1,7 @@ -package info.FrostFS.sdk.dto.netmap; +package info.frostfs.sdk.dto.netmap; -import info.FrostFS.sdk.dto.Version; -import info.FrostFS.sdk.enums.NodeState; +import info.frostfs.sdk.dto.Version; +import info.frostfs.sdk.enums.NodeState; import java.util.List; import java.util.Map; diff --git a/models/src/main/java/info/FrostFS/sdk/dto/netmap/PlacementPolicy.java b/models/src/main/java/info/frostfs/sdk/dto/netmap/PlacementPolicy.java similarity index 90% rename from models/src/main/java/info/FrostFS/sdk/dto/netmap/PlacementPolicy.java rename to models/src/main/java/info/frostfs/sdk/dto/netmap/PlacementPolicy.java index 9e92a63..74a1c72 100644 --- a/models/src/main/java/info/FrostFS/sdk/dto/netmap/PlacementPolicy.java +++ b/models/src/main/java/info/frostfs/sdk/dto/netmap/PlacementPolicy.java @@ -1,4 +1,4 @@ -package info.FrostFS.sdk.dto.netmap; +package info.frostfs.sdk.dto.netmap; public class PlacementPolicy { private final Replica[] replicas; diff --git a/models/src/main/java/info/FrostFS/sdk/dto/netmap/Replica.java b/models/src/main/java/info/frostfs/sdk/dto/netmap/Replica.java similarity index 94% rename from models/src/main/java/info/FrostFS/sdk/dto/netmap/Replica.java rename to models/src/main/java/info/frostfs/sdk/dto/netmap/Replica.java index 5189df7..235d288 100644 --- a/models/src/main/java/info/FrostFS/sdk/dto/netmap/Replica.java +++ b/models/src/main/java/info/frostfs/sdk/dto/netmap/Replica.java @@ -1,4 +1,4 @@ -package info.FrostFS.sdk.dto.netmap; +package info.frostfs.sdk.dto.netmap; import static java.util.Objects.isNull; diff --git a/models/src/main/java/info/FrostFS/sdk/dto/object/LargeObject.java b/models/src/main/java/info/frostfs/sdk/dto/object/LargeObject.java similarity index 81% rename from models/src/main/java/info/FrostFS/sdk/dto/object/LargeObject.java rename to models/src/main/java/info/frostfs/sdk/dto/object/LargeObject.java index 66d9ad0..4cab2ed 100644 --- a/models/src/main/java/info/FrostFS/sdk/dto/object/LargeObject.java +++ b/models/src/main/java/info/frostfs/sdk/dto/object/LargeObject.java @@ -1,10 +1,10 @@ -package info.FrostFS.sdk.dto.object; +package info.frostfs.sdk.dto.object; -import info.FrostFS.sdk.dto.container.ContainerId; +import info.frostfs.sdk.dto.container.ContainerId; import java.security.MessageDigest; -import static info.FrostFS.sdk.Helper.getSha256Instance; +import static info.frostfs.sdk.Helper.getSha256Instance; public class LargeObject extends ObjectFrostFS { private final MessageDigest payloadHash; diff --git a/models/src/main/java/info/FrostFS/sdk/dto/object/LinkObject.java b/models/src/main/java/info/frostfs/sdk/dto/object/LinkObject.java similarity index 74% rename from models/src/main/java/info/FrostFS/sdk/dto/object/LinkObject.java rename to models/src/main/java/info/frostfs/sdk/dto/object/LinkObject.java index e7b8ea4..b641888 100644 --- a/models/src/main/java/info/FrostFS/sdk/dto/object/LinkObject.java +++ b/models/src/main/java/info/frostfs/sdk/dto/object/LinkObject.java @@ -1,8 +1,8 @@ -package info.FrostFS.sdk.dto.object; +package info.frostfs.sdk.dto.object; -import info.FrostFS.sdk.dto.Split; -import info.FrostFS.sdk.dto.SplitId; -import info.FrostFS.sdk.dto.container.ContainerId; +import info.frostfs.sdk.dto.Split; +import info.frostfs.sdk.dto.SplitId; +import info.frostfs.sdk.dto.container.ContainerId; import java.util.List; diff --git a/models/src/main/java/info/FrostFS/sdk/dto/object/ObjectAttribute.java b/models/src/main/java/info/frostfs/sdk/dto/object/ObjectAttribute.java similarity index 92% rename from models/src/main/java/info/FrostFS/sdk/dto/object/ObjectAttribute.java rename to models/src/main/java/info/frostfs/sdk/dto/object/ObjectAttribute.java index 9729f01..0c2554b 100644 --- a/models/src/main/java/info/FrostFS/sdk/dto/object/ObjectAttribute.java +++ b/models/src/main/java/info/frostfs/sdk/dto/object/ObjectAttribute.java @@ -1,4 +1,4 @@ -package info.FrostFS.sdk.dto.object; +package info.frostfs.sdk.dto.object; public class ObjectAttribute { private String key; diff --git a/models/src/main/java/info/FrostFS/sdk/dto/object/ObjectFilter.java b/models/src/main/java/info/frostfs/sdk/dto/object/ObjectFilter.java similarity index 90% rename from models/src/main/java/info/FrostFS/sdk/dto/object/ObjectFilter.java rename to models/src/main/java/info/frostfs/sdk/dto/object/ObjectFilter.java index 0dae796..1443ee2 100644 --- a/models/src/main/java/info/FrostFS/sdk/dto/object/ObjectFilter.java +++ b/models/src/main/java/info/frostfs/sdk/dto/object/ObjectFilter.java @@ -1,8 +1,8 @@ -package info.FrostFS.sdk.dto.object; +package info.frostfs.sdk.dto.object; -import info.FrostFS.sdk.dto.OwnerId; -import info.FrostFS.sdk.dto.Version; -import info.FrostFS.sdk.enums.ObjectMatchType; +import info.frostfs.sdk.dto.OwnerId; +import info.frostfs.sdk.dto.Version; +import info.frostfs.sdk.enums.ObjectMatchType; public class ObjectFilter { private static final String HEADER_PREFIX = "$Object:"; diff --git a/models/src/main/java/info/FrostFS/sdk/dto/object/ObjectFrostFS.java b/models/src/main/java/info/frostfs/sdk/dto/object/ObjectFrostFS.java similarity index 92% rename from models/src/main/java/info/FrostFS/sdk/dto/object/ObjectFrostFS.java rename to models/src/main/java/info/frostfs/sdk/dto/object/ObjectFrostFS.java index 3b9d3c9..5001161 100644 --- a/models/src/main/java/info/FrostFS/sdk/dto/object/ObjectFrostFS.java +++ b/models/src/main/java/info/frostfs/sdk/dto/object/ObjectFrostFS.java @@ -1,8 +1,8 @@ -package info.FrostFS.sdk.dto.object; +package info.frostfs.sdk.dto.object; -import info.FrostFS.sdk.dto.Split; -import info.FrostFS.sdk.dto.container.ContainerId; -import info.FrostFS.sdk.enums.ObjectType; +import info.frostfs.sdk.dto.Split; +import info.frostfs.sdk.dto.container.ContainerId; +import info.frostfs.sdk.enums.ObjectType; import java.util.ArrayList; import java.util.List; diff --git a/models/src/main/java/info/FrostFS/sdk/dto/object/ObjectHeader.java b/models/src/main/java/info/frostfs/sdk/dto/object/ObjectHeader.java similarity index 91% rename from models/src/main/java/info/FrostFS/sdk/dto/object/ObjectHeader.java rename to models/src/main/java/info/frostfs/sdk/dto/object/ObjectHeader.java index 401ed41..147ec4e 100644 --- a/models/src/main/java/info/FrostFS/sdk/dto/object/ObjectHeader.java +++ b/models/src/main/java/info/frostfs/sdk/dto/object/ObjectHeader.java @@ -1,10 +1,10 @@ -package info.FrostFS.sdk.dto.object; +package info.frostfs.sdk.dto.object; -import info.FrostFS.sdk.dto.OwnerId; -import info.FrostFS.sdk.dto.Split; -import info.FrostFS.sdk.dto.Version; -import info.FrostFS.sdk.dto.container.ContainerId; -import info.FrostFS.sdk.enums.ObjectType; +import info.frostfs.sdk.dto.OwnerId; +import info.frostfs.sdk.dto.Split; +import info.frostfs.sdk.dto.Version; +import info.frostfs.sdk.dto.container.ContainerId; +import info.frostfs.sdk.enums.ObjectType; import java.util.List; diff --git a/models/src/main/java/info/FrostFS/sdk/dto/object/ObjectId.java b/models/src/main/java/info/frostfs/sdk/dto/object/ObjectId.java similarity index 83% rename from models/src/main/java/info/FrostFS/sdk/dto/object/ObjectId.java rename to models/src/main/java/info/frostfs/sdk/dto/object/ObjectId.java index 4b36d80..3c9965a 100644 --- a/models/src/main/java/info/FrostFS/sdk/dto/object/ObjectId.java +++ b/models/src/main/java/info/frostfs/sdk/dto/object/ObjectId.java @@ -1,7 +1,7 @@ -package info.FrostFS.sdk.dto.object; +package info.frostfs.sdk.dto.object; -import info.FrostFS.sdk.Base58; -import info.FrostFS.sdk.constants.AppConst; +import info.frostfs.sdk.Base58; +import info.frostfs.sdk.constants.AppConst; public class ObjectId { private final String value; diff --git a/models/src/main/java/info/FrostFS/sdk/enums/BasicAcl.java b/models/src/main/java/info/frostfs/sdk/enums/BasicAcl.java similarity index 95% rename from models/src/main/java/info/FrostFS/sdk/enums/BasicAcl.java rename to models/src/main/java/info/frostfs/sdk/enums/BasicAcl.java index 7ebf7f7..52bab99 100644 --- a/models/src/main/java/info/FrostFS/sdk/enums/BasicAcl.java +++ b/models/src/main/java/info/frostfs/sdk/enums/BasicAcl.java @@ -1,4 +1,4 @@ -package info.FrostFS.sdk.enums; +package info.frostfs.sdk.enums; import java.util.Collections; import java.util.HashMap; diff --git a/models/src/main/java/info/FrostFS/sdk/enums/NodeState.java b/models/src/main/java/info/frostfs/sdk/enums/NodeState.java similarity index 95% rename from models/src/main/java/info/FrostFS/sdk/enums/NodeState.java rename to models/src/main/java/info/frostfs/sdk/enums/NodeState.java index e6f1464..3f833e3 100644 --- a/models/src/main/java/info/FrostFS/sdk/enums/NodeState.java +++ b/models/src/main/java/info/frostfs/sdk/enums/NodeState.java @@ -1,4 +1,4 @@ -package info.FrostFS.sdk.enums; +package info.frostfs.sdk.enums; import java.util.Collections; import java.util.HashMap; diff --git a/models/src/main/java/info/FrostFS/sdk/enums/ObjectMatchType.java b/models/src/main/java/info/frostfs/sdk/enums/ObjectMatchType.java similarity index 87% rename from models/src/main/java/info/FrostFS/sdk/enums/ObjectMatchType.java rename to models/src/main/java/info/frostfs/sdk/enums/ObjectMatchType.java index 385756f..2122ad6 100644 --- a/models/src/main/java/info/FrostFS/sdk/enums/ObjectMatchType.java +++ b/models/src/main/java/info/frostfs/sdk/enums/ObjectMatchType.java @@ -1,4 +1,4 @@ -package info.FrostFS.sdk.enums; +package info.frostfs.sdk.enums; public enum ObjectMatchType { UNSPECIFIED(0), diff --git a/models/src/main/java/info/FrostFS/sdk/enums/ObjectType.java b/models/src/main/java/info/frostfs/sdk/enums/ObjectType.java similarity index 95% rename from models/src/main/java/info/FrostFS/sdk/enums/ObjectType.java rename to models/src/main/java/info/frostfs/sdk/enums/ObjectType.java index feb545f..d252a7a 100644 --- a/models/src/main/java/info/FrostFS/sdk/enums/ObjectType.java +++ b/models/src/main/java/info/frostfs/sdk/enums/ObjectType.java @@ -1,4 +1,4 @@ -package info.FrostFS.sdk.enums; +package info.frostfs.sdk.enums; import java.util.Collections; import java.util.HashMap; diff --git a/models/src/main/java/info/FrostFS/sdk/enums/SignatureScheme.java b/models/src/main/java/info/frostfs/sdk/enums/SignatureScheme.java similarity index 87% rename from models/src/main/java/info/FrostFS/sdk/enums/SignatureScheme.java rename to models/src/main/java/info/frostfs/sdk/enums/SignatureScheme.java index 362c75a..7bb7511 100644 --- a/models/src/main/java/info/FrostFS/sdk/enums/SignatureScheme.java +++ b/models/src/main/java/info/frostfs/sdk/enums/SignatureScheme.java @@ -1,4 +1,4 @@ -package info.FrostFS.sdk.enums; +package info.frostfs.sdk.enums; public enum SignatureScheme { ECDSA_SHA512(0), diff --git a/models/src/main/java/info/FrostFS/sdk/enums/StatusCode.java b/models/src/main/java/info/frostfs/sdk/enums/StatusCode.java similarity index 97% rename from models/src/main/java/info/FrostFS/sdk/enums/StatusCode.java rename to models/src/main/java/info/frostfs/sdk/enums/StatusCode.java index 4e44421..b67e766 100644 --- a/models/src/main/java/info/FrostFS/sdk/enums/StatusCode.java +++ b/models/src/main/java/info/frostfs/sdk/enums/StatusCode.java @@ -1,4 +1,4 @@ -package info.FrostFS.sdk.enums; +package info.frostfs.sdk.enums; import java.util.Collections; import java.util.HashMap; diff --git a/models/src/main/java/info/FrostFS/sdk/mappers/MetaHeaderMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/MetaHeaderMapper.java similarity index 78% rename from models/src/main/java/info/FrostFS/sdk/mappers/MetaHeaderMapper.java rename to models/src/main/java/info/frostfs/sdk/mappers/MetaHeaderMapper.java index 96371d7..736871c 100644 --- a/models/src/main/java/info/FrostFS/sdk/mappers/MetaHeaderMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/MetaHeaderMapper.java @@ -1,7 +1,7 @@ -package info.FrostFS.sdk.mappers; +package info.frostfs.sdk.mappers; -import frostFS.session.Types; -import info.FrostFS.sdk.dto.MetaHeader; +import frostfs.session.Types; +import info.frostfs.sdk.dto.MetaHeader; public class MetaHeaderMapper { diff --git a/models/src/main/java/info/FrostFS/sdk/mappers/OwnerIdMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/OwnerIdMapper.java similarity index 73% rename from models/src/main/java/info/FrostFS/sdk/mappers/OwnerIdMapper.java rename to models/src/main/java/info/frostfs/sdk/mappers/OwnerIdMapper.java index edf9f1c..69cb6e8 100644 --- a/models/src/main/java/info/FrostFS/sdk/mappers/OwnerIdMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/OwnerIdMapper.java @@ -1,8 +1,8 @@ -package info.FrostFS.sdk.mappers; +package info.frostfs.sdk.mappers; import com.google.protobuf.ByteString; -import frostFS.refs.Types; -import info.FrostFS.sdk.dto.OwnerId; +import frostfs.refs.Types; +import info.frostfs.sdk.dto.OwnerId; public class OwnerIdMapper { diff --git a/models/src/main/java/info/FrostFS/sdk/mappers/SessionMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/SessionMapper.java similarity index 93% rename from models/src/main/java/info/FrostFS/sdk/mappers/SessionMapper.java rename to models/src/main/java/info/frostfs/sdk/mappers/SessionMapper.java index a8d329d..c13a61e 100644 --- a/models/src/main/java/info/FrostFS/sdk/mappers/SessionMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/SessionMapper.java @@ -1,8 +1,8 @@ -package info.FrostFS.sdk.mappers; +package info.frostfs.sdk.mappers; import com.google.protobuf.CodedOutputStream; import com.google.protobuf.InvalidProtocolBufferException; -import frostFS.session.Types; +import frostfs.session.Types; import java.io.IOException; diff --git a/models/src/main/java/info/FrostFS/sdk/mappers/SignatureMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/SignatureMapper.java similarity index 87% rename from models/src/main/java/info/FrostFS/sdk/mappers/SignatureMapper.java rename to models/src/main/java/info/frostfs/sdk/mappers/SignatureMapper.java index 3a388d0..1a62bfd 100644 --- a/models/src/main/java/info/FrostFS/sdk/mappers/SignatureMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/SignatureMapper.java @@ -1,8 +1,8 @@ -package info.FrostFS.sdk.mappers; +package info.frostfs.sdk.mappers; import com.google.protobuf.ByteString; -import frostFS.refs.Types; -import info.FrostFS.sdk.dto.Signature; +import frostfs.refs.Types; +import info.frostfs.sdk.dto.Signature; import static java.util.Objects.isNull; diff --git a/models/src/main/java/info/FrostFS/sdk/mappers/StatusMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/StatusMapper.java similarity index 78% rename from models/src/main/java/info/FrostFS/sdk/mappers/StatusMapper.java rename to models/src/main/java/info/frostfs/sdk/mappers/StatusMapper.java index 3817f7d..7c6f3b1 100644 --- a/models/src/main/java/info/FrostFS/sdk/mappers/StatusMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/StatusMapper.java @@ -1,8 +1,8 @@ -package info.FrostFS.sdk.mappers; +package info.frostfs.sdk.mappers; -import frostFS.status.Types; -import info.FrostFS.sdk.dto.Status; -import info.FrostFS.sdk.enums.StatusCode; +import frostfs.status.Types; +import info.frostfs.sdk.dto.Status; +import info.frostfs.sdk.enums.StatusCode; import static java.util.Objects.isNull; diff --git a/models/src/main/java/info/FrostFS/sdk/mappers/VersionMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/VersionMapper.java similarity index 80% rename from models/src/main/java/info/FrostFS/sdk/mappers/VersionMapper.java rename to models/src/main/java/info/frostfs/sdk/mappers/VersionMapper.java index fefb7ba..7888a67 100644 --- a/models/src/main/java/info/FrostFS/sdk/mappers/VersionMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/VersionMapper.java @@ -1,7 +1,7 @@ -package info.FrostFS.sdk.mappers; +package info.frostfs.sdk.mappers; -import frostFS.refs.Types; -import info.FrostFS.sdk.dto.Version; +import frostfs.refs.Types; +import info.frostfs.sdk.dto.Version; public class VersionMapper { diff --git a/models/src/main/java/info/FrostFS/sdk/mappers/container/ContainerIdMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerIdMapper.java similarity index 71% rename from models/src/main/java/info/FrostFS/sdk/mappers/container/ContainerIdMapper.java rename to models/src/main/java/info/frostfs/sdk/mappers/container/ContainerIdMapper.java index b661e71..cbe7237 100644 --- a/models/src/main/java/info/FrostFS/sdk/mappers/container/ContainerIdMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerIdMapper.java @@ -1,8 +1,8 @@ -package info.FrostFS.sdk.mappers.container; +package info.frostfs.sdk.mappers.container; import com.google.protobuf.ByteString; -import frostFS.refs.Types; -import info.FrostFS.sdk.dto.container.ContainerId; +import frostfs.refs.Types; +import info.frostfs.sdk.dto.container.ContainerId; public class ContainerIdMapper { diff --git a/models/src/main/java/info/FrostFS/sdk/mappers/container/ContainerMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerMapper.java similarity index 75% rename from models/src/main/java/info/FrostFS/sdk/mappers/container/ContainerMapper.java rename to models/src/main/java/info/frostfs/sdk/mappers/container/ContainerMapper.java index 9895eee..ff36486 100644 --- a/models/src/main/java/info/FrostFS/sdk/mappers/container/ContainerMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerMapper.java @@ -1,14 +1,14 @@ -package info.FrostFS.sdk.mappers.container; +package info.frostfs.sdk.mappers.container; import com.google.protobuf.ByteString; -import frostFS.container.Types; -import info.FrostFS.sdk.dto.container.Container; -import info.FrostFS.sdk.enums.BasicAcl; -import info.FrostFS.sdk.mappers.VersionMapper; -import info.FrostFS.sdk.mappers.netmap.PlacementPolicyMapper; +import frostfs.container.Types; +import info.frostfs.sdk.dto.container.Container; +import info.frostfs.sdk.enums.BasicAcl; +import info.frostfs.sdk.mappers.VersionMapper; +import info.frostfs.sdk.mappers.netmap.PlacementPolicyMapper; -import static info.FrostFS.sdk.UUIDExtension.asBytes; -import static info.FrostFS.sdk.UUIDExtension.asUuid; +import static info.frostfs.sdk.UUIDExtension.asBytes; +import static info.frostfs.sdk.UUIDExtension.asUuid; import static java.util.Objects.isNull; public class ContainerMapper { diff --git a/models/src/main/java/info/FrostFS/sdk/mappers/netmap/NetmapSnapshotMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/netmap/NetmapSnapshotMapper.java similarity index 80% rename from models/src/main/java/info/FrostFS/sdk/mappers/netmap/NetmapSnapshotMapper.java rename to models/src/main/java/info/frostfs/sdk/mappers/netmap/NetmapSnapshotMapper.java index d18fc32..a396f58 100644 --- a/models/src/main/java/info/FrostFS/sdk/mappers/netmap/NetmapSnapshotMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/netmap/NetmapSnapshotMapper.java @@ -1,7 +1,7 @@ -package info.FrostFS.sdk.mappers.netmap; +package info.frostfs.sdk.mappers.netmap; -import frostFS.netmap.Service; -import info.FrostFS.sdk.dto.netmap.NetmapSnapshot; +import frostfs.netmap.Service; +import info.frostfs.sdk.dto.netmap.NetmapSnapshot; import java.util.stream.Collectors; diff --git a/models/src/main/java/info/FrostFS/sdk/mappers/netmap/NodeInfoMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/netmap/NodeInfoMapper.java similarity index 72% rename from models/src/main/java/info/FrostFS/sdk/mappers/netmap/NodeInfoMapper.java rename to models/src/main/java/info/frostfs/sdk/mappers/netmap/NodeInfoMapper.java index 53eca35..c2bd9b7 100644 --- a/models/src/main/java/info/FrostFS/sdk/mappers/netmap/NodeInfoMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/netmap/NodeInfoMapper.java @@ -1,11 +1,11 @@ -package info.FrostFS.sdk.mappers.netmap; +package info.frostfs.sdk.mappers.netmap; -import frostFS.netmap.Service; -import frostFS.netmap.Types.NodeInfo.Attribute; -import frostFS.refs.Types; -import info.FrostFS.sdk.dto.netmap.NodeInfo; -import info.FrostFS.sdk.enums.NodeState; -import info.FrostFS.sdk.mappers.VersionMapper; +import frostfs.netmap.Service; +import frostfs.netmap.Types.NodeInfo.Attribute; +import frostfs.refs.Types; +import info.frostfs.sdk.dto.netmap.NodeInfo; +import info.frostfs.sdk.enums.NodeState; +import info.frostfs.sdk.mappers.VersionMapper; import java.util.stream.Collectors; @@ -17,7 +17,7 @@ public class NodeInfoMapper { return toModel(nodeInfo.getNodeInfo(), nodeInfo.getVersion()); } - public static NodeInfo toModel(frostFS.netmap.Types.NodeInfo nodeInfo, Types.Version version) { + public static NodeInfo toModel(frostfs.netmap.Types.NodeInfo nodeInfo, Types.Version version) { NodeState nodeState = NodeState.get(nodeInfo.getState().getNumber()); if (isNull(nodeState)) { throw new IllegalArgumentException( diff --git a/models/src/main/java/info/FrostFS/sdk/mappers/netmap/PlacementPolicyMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/netmap/PlacementPolicyMapper.java similarity index 80% rename from models/src/main/java/info/FrostFS/sdk/mappers/netmap/PlacementPolicyMapper.java rename to models/src/main/java/info/frostfs/sdk/mappers/netmap/PlacementPolicyMapper.java index e2803d6..4a6675c 100644 --- a/models/src/main/java/info/FrostFS/sdk/mappers/netmap/PlacementPolicyMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/netmap/PlacementPolicyMapper.java @@ -1,8 +1,8 @@ -package info.FrostFS.sdk.mappers.netmap; +package info.frostfs.sdk.mappers.netmap; -import frostFS.netmap.Types; -import info.FrostFS.sdk.dto.netmap.PlacementPolicy; -import info.FrostFS.sdk.dto.netmap.Replica; +import frostfs.netmap.Types; +import info.frostfs.sdk.dto.netmap.PlacementPolicy; +import info.frostfs.sdk.dto.netmap.Replica; public class PlacementPolicyMapper { diff --git a/models/src/main/java/info/FrostFS/sdk/mappers/netmap/ReplicaMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/netmap/ReplicaMapper.java similarity index 78% rename from models/src/main/java/info/FrostFS/sdk/mappers/netmap/ReplicaMapper.java rename to models/src/main/java/info/frostfs/sdk/mappers/netmap/ReplicaMapper.java index b2929ba..eef3e70 100644 --- a/models/src/main/java/info/FrostFS/sdk/mappers/netmap/ReplicaMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/netmap/ReplicaMapper.java @@ -1,7 +1,7 @@ -package info.FrostFS.sdk.mappers.netmap; +package info.frostfs.sdk.mappers.netmap; -import frostFS.netmap.Types; -import info.FrostFS.sdk.dto.netmap.Replica; +import frostfs.netmap.Types; +import info.frostfs.sdk.dto.netmap.Replica; public class ReplicaMapper { diff --git a/models/src/main/java/info/FrostFS/sdk/mappers/object/ObjectAttributeMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectAttributeMapper.java similarity index 79% rename from models/src/main/java/info/FrostFS/sdk/mappers/object/ObjectAttributeMapper.java rename to models/src/main/java/info/frostfs/sdk/mappers/object/ObjectAttributeMapper.java index 7604e0f..62c67cb 100644 --- a/models/src/main/java/info/FrostFS/sdk/mappers/object/ObjectAttributeMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectAttributeMapper.java @@ -1,7 +1,7 @@ -package info.FrostFS.sdk.mappers.object; +package info.frostfs.sdk.mappers.object; -import frostFS.object.Types; -import info.FrostFS.sdk.dto.object.ObjectAttribute; +import frostfs.object.Types; +import info.frostfs.sdk.dto.object.ObjectAttribute; public class ObjectAttributeMapper { diff --git a/models/src/main/java/info/FrostFS/sdk/mappers/object/ObjectFilterMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectFilterMapper.java similarity index 82% rename from models/src/main/java/info/FrostFS/sdk/mappers/object/ObjectFilterMapper.java rename to models/src/main/java/info/frostfs/sdk/mappers/object/ObjectFilterMapper.java index 52adf8d..8bbe23e 100644 --- a/models/src/main/java/info/FrostFS/sdk/mappers/object/ObjectFilterMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectFilterMapper.java @@ -1,8 +1,8 @@ -package info.FrostFS.sdk.mappers.object; +package info.frostfs.sdk.mappers.object; -import frostFS.object.Service; -import frostFS.object.Types; -import info.FrostFS.sdk.dto.object.ObjectFilter; +import frostfs.object.Service; +import frostfs.object.Types; +import info.frostfs.sdk.dto.object.ObjectFilter; import static java.util.Objects.isNull; diff --git a/models/src/main/java/info/FrostFS/sdk/mappers/object/ObjectFrostFSMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectFrostFSMapper.java similarity index 67% rename from models/src/main/java/info/FrostFS/sdk/mappers/object/ObjectFrostFSMapper.java rename to models/src/main/java/info/frostfs/sdk/mappers/object/ObjectFrostFSMapper.java index 22cc305..28b8332 100644 --- a/models/src/main/java/info/FrostFS/sdk/mappers/object/ObjectFrostFSMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectFrostFSMapper.java @@ -1,8 +1,8 @@ -package info.FrostFS.sdk.mappers.object; +package info.frostfs.sdk.mappers.object; -import frostFS.object.Types; -import info.FrostFS.sdk.dto.object.ObjectFrostFS; -import info.FrostFS.sdk.dto.object.ObjectId; +import frostfs.object.Types; +import info.frostfs.sdk.dto.object.ObjectFrostFS; +import info.frostfs.sdk.dto.object.ObjectId; public class ObjectFrostFSMapper { diff --git a/models/src/main/java/info/FrostFS/sdk/mappers/object/ObjectHeaderMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectHeaderMapper.java similarity index 80% rename from models/src/main/java/info/FrostFS/sdk/mappers/object/ObjectHeaderMapper.java rename to models/src/main/java/info/frostfs/sdk/mappers/object/ObjectHeaderMapper.java index d47dc55..6b8bc91 100644 --- a/models/src/main/java/info/FrostFS/sdk/mappers/object/ObjectHeaderMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectHeaderMapper.java @@ -1,12 +1,12 @@ -package info.FrostFS.sdk.mappers.object; +package info.frostfs.sdk.mappers.object; -import frostFS.object.Types; -import info.FrostFS.sdk.dto.container.ContainerId; -import info.FrostFS.sdk.dto.object.ObjectAttribute; -import info.FrostFS.sdk.dto.object.ObjectHeader; -import info.FrostFS.sdk.enums.ObjectType; -import info.FrostFS.sdk.mappers.VersionMapper; -import info.FrostFS.sdk.mappers.container.ContainerIdMapper; +import frostfs.object.Types; +import info.frostfs.sdk.dto.container.ContainerId; +import info.frostfs.sdk.dto.object.ObjectAttribute; +import info.frostfs.sdk.dto.object.ObjectHeader; +import info.frostfs.sdk.enums.ObjectType; +import info.frostfs.sdk.mappers.VersionMapper; +import info.frostfs.sdk.mappers.container.ContainerIdMapper; import java.util.stream.Collectors; diff --git a/models/src/main/java/info/FrostFS/sdk/mappers/object/ObjectIdMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectIdMapper.java similarity index 78% rename from models/src/main/java/info/FrostFS/sdk/mappers/object/ObjectIdMapper.java rename to models/src/main/java/info/frostfs/sdk/mappers/object/ObjectIdMapper.java index 437b4bb..62ca7c5 100644 --- a/models/src/main/java/info/FrostFS/sdk/mappers/object/ObjectIdMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectIdMapper.java @@ -1,8 +1,8 @@ -package info.FrostFS.sdk.mappers.object; +package info.frostfs.sdk.mappers.object; import com.google.protobuf.ByteString; -import frostFS.refs.Types; -import info.FrostFS.sdk.dto.object.ObjectId; +import frostfs.refs.Types; +import info.frostfs.sdk.dto.object.ObjectId; public class ObjectIdMapper { diff --git a/pom.xml b/pom.xml index 8423cfb..561413c 100644 --- a/pom.xml +++ b/pom.xml @@ -4,8 +4,8 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - info.FrostFS.sdk - FrostFS-sdk-java + info.frostfs.sdk + frostfs-sdk-java 0.1.0 pom diff --git a/protos/pom.xml b/protos/pom.xml index cee5fb0..d2e06dc 100644 --- a/protos/pom.xml +++ b/protos/pom.xml @@ -4,8 +4,8 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - info.FrostFS.sdk - FrostFS-sdk-java + info.frostfs.sdk + frostfs-sdk-java 0.1.0 diff --git a/protos/src/main/proto/accounting/service.proto b/protos/src/main/proto/accounting/service.proto index c225c58..414d71d 100644 --- a/protos/src/main/proto/accounting/service.proto +++ b/protos/src/main/proto/accounting/service.proto @@ -3,7 +3,7 @@ syntax = "proto3"; package neo.fs.v2.accounting; option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/accounting/grpc;accounting"; -option java_package = "frostFS.accounting"; +option java_package = "frostfs.accounting"; import "accounting/types.proto"; import "refs/types.proto"; diff --git a/protos/src/main/proto/accounting/types.proto b/protos/src/main/proto/accounting/types.proto index 57229d9..61952f5 100644 --- a/protos/src/main/proto/accounting/types.proto +++ b/protos/src/main/proto/accounting/types.proto @@ -3,7 +3,7 @@ syntax = "proto3"; package neo.fs.v2.accounting; option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/accounting/grpc;accounting"; -option java_package = "frostFS.accounting"; +option java_package = "frostfs.accounting"; // Standard floating point data type can't be used in NeoFS due to inexactness // of the result when doing lots of small number operations. To solve the lost diff --git a/protos/src/main/proto/acl/types.proto b/protos/src/main/proto/acl/types.proto index 86a9311..b59ac7d 100644 --- a/protos/src/main/proto/acl/types.proto +++ b/protos/src/main/proto/acl/types.proto @@ -3,7 +3,7 @@ syntax = "proto3"; package neo.fs.v2.acl; option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl/grpc;acl"; -option java_package = "frostFS.acl"; +option java_package = "frostfs.acl"; import "refs/types.proto"; diff --git a/protos/src/main/proto/apemanager/service.proto b/protos/src/main/proto/apemanager/service.proto index da0da48..d4eeca2 100644 --- a/protos/src/main/proto/apemanager/service.proto +++ b/protos/src/main/proto/apemanager/service.proto @@ -6,7 +6,7 @@ import "apemanager/types.proto"; import "session/types.proto"; option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/apemanager/grpc;apemanager"; -option java_package = "frostFS.apemanager"; +option java_package = "frostfs.apemanager"; // `APEManagerService` provides API to manage rule chains within sidechain's // `Policy` smart contract. diff --git a/protos/src/main/proto/apemanager/types.proto b/protos/src/main/proto/apemanager/types.proto index 644a8b5..7ca80ae 100644 --- a/protos/src/main/proto/apemanager/types.proto +++ b/protos/src/main/proto/apemanager/types.proto @@ -3,7 +3,7 @@ syntax = "proto3"; package frostfs.v2.apemanager; option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/apemanager/grpc;apemanager"; -option java_package = "frostFS.apemanager"; +option java_package = "frostfs.apemanager"; // TargetType is a type target to which a rule chain is defined. enum TargetType { diff --git a/protos/src/main/proto/container/service.proto b/protos/src/main/proto/container/service.proto index ce7634a..6a85979 100644 --- a/protos/src/main/proto/container/service.proto +++ b/protos/src/main/proto/container/service.proto @@ -3,7 +3,7 @@ syntax = "proto3"; package neo.fs.v2.container; option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container/grpc;container"; -option java_package = "frostFS.container"; +option java_package = "frostfs.container"; import "acl/types.proto"; import "container/types.proto"; diff --git a/protos/src/main/proto/container/types.proto b/protos/src/main/proto/container/types.proto index 128a0bd..fc523ca 100644 --- a/protos/src/main/proto/container/types.proto +++ b/protos/src/main/proto/container/types.proto @@ -3,7 +3,7 @@ syntax = "proto3"; package neo.fs.v2.container; option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container/grpc;container"; -option java_package = "frostFS.container"; +option java_package = "frostfs.container"; import "netmap/types.proto"; import "refs/types.proto"; diff --git a/protos/src/main/proto/lock/types.proto b/protos/src/main/proto/lock/types.proto index 85766db..e4a8879 100644 --- a/protos/src/main/proto/lock/types.proto +++ b/protos/src/main/proto/lock/types.proto @@ -3,7 +3,7 @@ syntax = "proto3"; package neo.fs.v2.lock; option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/lock/grpc;lock"; -option java_package = "frostFS.lock"; +option java_package = "frostfs.lock"; import "refs/types.proto"; diff --git a/protos/src/main/proto/netmap/service.proto b/protos/src/main/proto/netmap/service.proto index ac4f4cc..7e97e09 100644 --- a/protos/src/main/proto/netmap/service.proto +++ b/protos/src/main/proto/netmap/service.proto @@ -3,7 +3,7 @@ syntax = "proto3"; package neo.fs.v2.netmap; option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap/grpc;netmap"; -option java_package = "frostFS.netmap"; +option java_package = "frostfs.netmap"; import "netmap/types.proto"; import "refs/types.proto"; diff --git a/protos/src/main/proto/netmap/types.proto b/protos/src/main/proto/netmap/types.proto index 1ff2e9e..3c311ba 100644 --- a/protos/src/main/proto/netmap/types.proto +++ b/protos/src/main/proto/netmap/types.proto @@ -3,7 +3,7 @@ syntax = "proto3"; package neo.fs.v2.netmap; option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap/grpc;netmap"; -option java_package = "frostFS.netmap"; +option java_package = "frostfs.netmap"; // Operations on filters enum Operation { diff --git a/protos/src/main/proto/object/service.proto b/protos/src/main/proto/object/service.proto index 98ce6f0..7635f8a 100644 --- a/protos/src/main/proto/object/service.proto +++ b/protos/src/main/proto/object/service.proto @@ -3,7 +3,7 @@ syntax = "proto3"; package neo.fs.v2.object; option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object/grpc;object"; -option java_package = "frostFS.object"; +option java_package = "frostfs.object"; import "object/types.proto"; import "refs/types.proto"; diff --git a/protos/src/main/proto/object/types.proto b/protos/src/main/proto/object/types.proto index 3162270..623c2a3 100644 --- a/protos/src/main/proto/object/types.proto +++ b/protos/src/main/proto/object/types.proto @@ -3,7 +3,7 @@ syntax = "proto3"; package neo.fs.v2.object; option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object/grpc;object"; -option java_package = "frostFS.object"; +option java_package = "frostfs.object"; import "refs/types.proto"; import "session/types.proto"; diff --git a/protos/src/main/proto/refs/types.proto b/protos/src/main/proto/refs/types.proto index 30cb552..0ed5840 100644 --- a/protos/src/main/proto/refs/types.proto +++ b/protos/src/main/proto/refs/types.proto @@ -3,7 +3,7 @@ syntax = "proto3"; package neo.fs.v2.refs; option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs/grpc;refs"; -option java_package = "frostFS.refs"; +option java_package = "frostfs.refs"; // Objects in NeoFS are addressed by their ContainerID and ObjectID. // diff --git a/protos/src/main/proto/session/service.proto b/protos/src/main/proto/session/service.proto index 84cbff4..b31bd8e 100644 --- a/protos/src/main/proto/session/service.proto +++ b/protos/src/main/proto/session/service.proto @@ -3,7 +3,7 @@ syntax = "proto3"; package neo.fs.v2.session; option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session/grpc;session"; -option java_package = "frostFS.session"; +option java_package = "frostfs.session"; import "refs/types.proto"; import "session/types.proto"; diff --git a/protos/src/main/proto/session/types.proto b/protos/src/main/proto/session/types.proto index 2b33386..9f5259a 100644 --- a/protos/src/main/proto/session/types.proto +++ b/protos/src/main/proto/session/types.proto @@ -3,7 +3,7 @@ syntax = "proto3"; package neo.fs.v2.session; option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session/grpc;session"; -option java_package = "frostFS.session"; +option java_package = "frostfs.session"; import "refs/types.proto"; import "acl/types.proto"; diff --git a/protos/src/main/proto/status/types.proto b/protos/src/main/proto/status/types.proto index b7be372..7d8f8e9 100644 --- a/protos/src/main/proto/status/types.proto +++ b/protos/src/main/proto/status/types.proto @@ -3,7 +3,7 @@ syntax = "proto3"; package neo.fs.v2.status; option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/status/grpc;status"; -option java_package = "frostFS.status"; +option java_package = "frostfs.status"; // Declares the general format of the status returns of the NeoFS RPC protocol. // Status is present in all response messages. Each RPC of NeoFS protocol diff --git a/protos/src/main/proto/tombstone/types.proto b/protos/src/main/proto/tombstone/types.proto index 3e821c1..9128160 100644 --- a/protos/src/main/proto/tombstone/types.proto +++ b/protos/src/main/proto/tombstone/types.proto @@ -3,7 +3,7 @@ syntax = "proto3"; package neo.fs.v2.tombstone; option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/tombstone/grpc;tombstone"; -option java_package = "frostFS.tombstone"; +option java_package = "frostfs.tombstone"; import "refs/types.proto"; From 1be65c63aed28d9dbfd48ec8428aff52ed4c20f6 Mon Sep 17 00:00:00 2001 From: Bruk Ori Date: Wed, 24 Jul 2024 14:41:42 +0300 Subject: [PATCH 08/38] [#1] Add additional security Signed-off-by: Ori Bruk --- README.md | 4 +- client/pom.xml | 4 +- .../sdk/{services => }/FrostFSClient.java | 57 ++++++------ .../frostfs/sdk/constants/CryptoConst.java | 3 + .../frostfs/sdk/jdo/ClientEnvironment.java | 11 ++- .../info/frostfs/sdk/jdo/ClientSettings.java | 8 ++ .../main/java/info/frostfs/sdk/jdo/ECDsa.java | 6 ++ .../frostfs/sdk/jdo/PutObjectParameters.java | 4 + ...rService.java => ContainerClientImpl.java} | 84 +++++++++-------- ...tmapService.java => NetmapClientImpl.java} | 16 ++-- ...jectService.java => ObjectClientImpl.java} | 93 ++++++++++--------- ...{ObjectTools.java => ObjectToolsImpl.java} | 30 +++--- ...ionService.java => SessionClientImpl.java} | 17 ++-- .../impl/{ => rwhelper}/ObjectReader.java | 2 +- .../impl/{ => rwhelper}/ObjectWriter.java | 2 +- .../impl/{ => rwhelper}/SearchReader.java | 2 +- .../info/frostfs/sdk/tools/GrpcClient.java | 3 + .../info/frostfs/sdk/tools/MessageHelper.java | 3 + .../frostfs/sdk/tools/RequestConstructor.java | 15 ++- .../info/frostfs/sdk/tools/RequestSigner.java | 5 +- .../java/info/frostfs/sdk/tools/Verifier.java | 23 ++++- cryptography/pom.xml | 5 + .../java/info/frostfs/sdk/ArrayHelper.java | 2 + .../main/java/info/frostfs/sdk/Base58.java | 18 +++- .../main/java/info/frostfs/sdk/Helper.java | 24 ++++- .../java/info/frostfs/sdk/KeyExtension.java | 31 ++++++- models/pom.xml | 15 ++- .../java/info/frostfs/sdk/UUIDExtension.java | 20 +++- .../info/frostfs/sdk/constants/AppConst.java | 5 + .../frostfs/sdk/constants/FieldConst.java | 4 + .../frostfs/sdk/constants/XHeaderConst.java | 3 + .../java/info/frostfs/sdk/dto/MetaHeader.java | 26 +++++- .../java/info/frostfs/sdk/dto/OwnerId.java | 9 +- .../main/java/info/frostfs/sdk/dto/Split.java | 12 ++- .../java/info/frostfs/sdk/dto/SplitId.java | 20 ++-- .../java/info/frostfs/sdk/dto/Status.java | 5 +- .../java/info/frostfs/sdk/dto/Version.java | 25 +++-- .../sdk/dto/container/ContainerId.java | 22 +++-- .../info/frostfs/sdk/dto/netmap/Replica.java | 28 +++--- .../frostfs/sdk/dto/object/LargeObject.java | 5 + .../frostfs/sdk/dto/object/LinkObject.java | 5 + .../sdk/dto/object/ObjectAttribute.java | 18 ++-- .../frostfs/sdk/dto/object/ObjectFrostFS.java | 18 ++-- .../frostfs/sdk/dto/object/ObjectHeader.java | 34 ++++--- .../info/frostfs/sdk/dto/object/ObjectId.java | 18 +++- .../frostfs/sdk/mappers/MetaHeaderMapper.java | 9 ++ .../frostfs/sdk/mappers/OwnerIdMapper.java | 9 ++ .../frostfs/sdk/mappers/SessionMapper.java | 13 +++ .../frostfs/sdk/mappers/SignatureMapper.java | 9 +- .../frostfs/sdk/mappers/StatusMapper.java | 7 +- .../frostfs/sdk/mappers/VersionMapper.java | 13 +++ .../mappers/container/ContainerIdMapper.java | 9 ++ .../mappers/container/ContainerMapper.java | 11 +++ .../mappers/netmap/NetmapSnapshotMapper.java | 9 ++ .../sdk/mappers/netmap/NodeInfoMapper.java | 11 +++ .../mappers/netmap/PlacementPolicyMapper.java | 13 +++ .../sdk/mappers/netmap/ReplicaMapper.java | 13 +++ .../mappers/object/ObjectAttributeMapper.java | 13 +++ .../mappers/object/ObjectFilterMapper.java | 7 ++ .../mappers/object/ObjectFrostFSMapper.java | 17 +++- .../mappers/object/ObjectHeaderMapper.java | 13 ++- .../sdk/mappers/object/ObjectIdMapper.java | 11 ++- 62 files changed, 670 insertions(+), 281 deletions(-) rename client/src/main/java/info/frostfs/sdk/{services => }/FrostFSClient.java (65%) rename client/src/main/java/info/frostfs/sdk/services/impl/{ContainerService.java => ContainerClientImpl.java} (58%) rename client/src/main/java/info/frostfs/sdk/services/impl/{NetmapService.java => NetmapClientImpl.java} (91%) rename client/src/main/java/info/frostfs/sdk/services/impl/{ObjectService.java => ObjectClientImpl.java} (81%) rename client/src/main/java/info/frostfs/sdk/services/impl/{ObjectTools.java => ObjectToolsImpl.java} (79%) rename client/src/main/java/info/frostfs/sdk/services/impl/{SessionService.java => SessionClientImpl.java} (79%) rename client/src/main/java/info/frostfs/sdk/services/impl/{ => rwhelper}/ObjectReader.java (96%) rename client/src/main/java/info/frostfs/sdk/services/impl/{ => rwhelper}/ObjectWriter.java (97%) rename client/src/main/java/info/frostfs/sdk/services/impl/{ => rwhelper}/SearchReader.java (91%) diff --git a/README.md b/README.md index ff4652e..bd5444b 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ import info.frostfs.sdk.dto.netmap.PlacementPolicy; import info.frostfs.sdk.dto.netmap.Replica; import info.frostfs.sdk.enums.BasicAcl; import info.frostfs.sdk.jdo.ClientSettings; -import info.frostfs.sdk.services.FrostFSClient; +import info.frostfs.sdk.FrostFSClient; public class ContainerExample { @@ -59,7 +59,7 @@ import info.frostfs.sdk.dto.object.ObjectAttribute; import info.frostfs.sdk.dto.object.ObjectFilter; import info.frostfs.sdk.dto.object.ObjectHeader; import info.frostfs.sdk.jdo.PutObjectParameters; -import info.frostfs.sdk.services.FrostFSClient; +import info.frostfs.sdk.FrostFSClient; import java.io.FileInputStream; import java.io.IOException; diff --git a/client/pom.xml b/client/pom.xml index 917fc46..bb808ce 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -1,6 +1,6 @@ - 4.0.0 diff --git a/client/src/main/java/info/frostfs/sdk/services/FrostFSClient.java b/client/src/main/java/info/frostfs/sdk/FrostFSClient.java similarity index 65% rename from client/src/main/java/info/frostfs/sdk/services/FrostFSClient.java rename to client/src/main/java/info/frostfs/sdk/FrostFSClient.java index da87a20..48fdcc5 100644 --- a/client/src/main/java/info/frostfs/sdk/services/FrostFSClient.java +++ b/client/src/main/java/info/frostfs/sdk/FrostFSClient.java @@ -1,4 +1,4 @@ -package info.frostfs.sdk.services; +package info.frostfs.sdk; import frostfs.session.Types; import info.frostfs.sdk.dto.SessionToken; @@ -15,6 +15,7 @@ import info.frostfs.sdk.jdo.ClientEnvironment; import info.frostfs.sdk.jdo.ClientSettings; import info.frostfs.sdk.jdo.NetworkSettings; import info.frostfs.sdk.jdo.PutObjectParameters; +import info.frostfs.sdk.services.*; import info.frostfs.sdk.services.impl.*; import io.grpc.Channel; @@ -27,11 +28,11 @@ public class FrostFSClient implements ContainerClient, ObjectClient, NetmapClien private static final String ERROR_CLIENT_OPTIONS_INIT = "Options must be initialized."; private static final String ERROR_VERSION_SUPPORT_TEMPLATE = "FrostFS %s is not supported."; - private final ContainerService containerService; - private final NetmapService netmapService; - private final ObjectService objectService; - private final SessionService sessionService; - private final ObjectTools objectTools; + private final ContainerClientImpl containerClientImpl; + private final NetmapClientImpl netmapClientImpl; + private final ObjectClientImpl objectClientImpl; + private final SessionClientImpl sessionClientImpl; + private final ObjectToolsImpl objectToolsImpl; public FrostFSClient(ClientSettings clientSettings) { if (isNull(clientSettings)) { @@ -43,18 +44,18 @@ public class FrostFSClient implements ContainerClient, ObjectClient, NetmapClien Channel channel = initGrpcChannel(clientSettings.getHost(), clientSettings.getCreds()); ClientEnvironment clientEnvironment = - new ClientEnvironment(clientSettings.getKey(), channel, new Version(2, 13), this); + new ClientEnvironment(clientSettings.getKey(), channel, new Version(), this); - this.containerService = new ContainerService(clientEnvironment); - this.netmapService = new NetmapService(clientEnvironment); - this.sessionService = new SessionService(clientEnvironment); - this.objectService = new ObjectService(clientEnvironment); - this.objectTools = new ObjectTools(clientEnvironment); + this.containerClientImpl = new ContainerClientImpl(clientEnvironment); + this.netmapClientImpl = new NetmapClientImpl(clientEnvironment); + this.sessionClientImpl = new SessionClientImpl(clientEnvironment); + this.objectClientImpl = new ObjectClientImpl(clientEnvironment); + this.objectToolsImpl = new ObjectToolsImpl(clientEnvironment); checkFrostFsVersionSupport(clientEnvironment.getVersion()); } private void checkFrostFsVersionSupport(Version version) { - var localNodeInfo = netmapService.getLocalNodeInfo(); + var localNodeInfo = netmapClientImpl.getLocalNodeInfo(); if (!localNodeInfo.getVersion().isSupported(version)) { throw new IllegalArgumentException( String.format(ERROR_VERSION_SUPPORT_TEMPLATE, localNodeInfo.getVersion()) @@ -64,75 +65,75 @@ public class FrostFSClient implements ContainerClient, ObjectClient, NetmapClien @Override public Container getContainer(ContainerId cid) { - return containerService.getContainer(cid); + return containerClientImpl.getContainer(cid); } @Override public List listContainers() { - return containerService.listContainers(); + return containerClientImpl.listContainers(); } @Override public ContainerId createContainer(Container container) { - return containerService.createContainer(container); + return containerClientImpl.createContainer(container); } @Override public void deleteContainer(ContainerId cid) { - containerService.deleteContainer(cid); + containerClientImpl.deleteContainer(cid); } @Override public ObjectHeader getObjectHead(ContainerId containerId, ObjectId objectId) { - return objectService.getObjectHead(containerId, objectId); + return objectClientImpl.getObjectHead(containerId, objectId); } @Override public ObjectFrostFS getObject(ContainerId containerId, ObjectId objectId) { - return objectService.getObject(containerId, objectId); + return objectClientImpl.getObject(containerId, objectId); } @Override public ObjectId putObject(PutObjectParameters parameters) { - return objectService.putObject(parameters); + return objectClientImpl.putObject(parameters); } @Override public void deleteObject(ContainerId containerId, ObjectId objectId) { - objectService.deleteObject(containerId, objectId); + objectClientImpl.deleteObject(containerId, objectId); } @Override public Iterable searchObjects(ContainerId cid, ObjectFilter... filters) { - return objectService.searchObjects(cid, filters); + return objectClientImpl.searchObjects(cid, filters); } @Override public NetmapSnapshot getNetmapSnapshot() { - return netmapService.getNetmapSnapshot(); + return netmapClientImpl.getNetmapSnapshot(); } @Override public NodeInfo getLocalNodeInfo() { - return netmapService.getLocalNodeInfo(); + return netmapClientImpl.getLocalNodeInfo(); } @Override public NetworkSettings getNetworkSettings() { - return netmapService.getNetworkSettings(); + return netmapClientImpl.getNetworkSettings(); } @Override public SessionToken createSession(long expiration) { - return sessionService.createSession(expiration); + return sessionClientImpl.createSession(expiration); } public Types.SessionToken createSessionInternal(long expiration) { - return sessionService.createSessionInternal(expiration); + return sessionClientImpl.createSessionInternal(expiration); } @Override public ObjectId calculateObjectId(ObjectHeader header) { - return objectTools.calculateObjectId(header); + return objectToolsImpl.calculateObjectId(header); } } diff --git a/client/src/main/java/info/frostfs/sdk/constants/CryptoConst.java b/client/src/main/java/info/frostfs/sdk/constants/CryptoConst.java index 84db5c9..4e28686 100644 --- a/client/src/main/java/info/frostfs/sdk/constants/CryptoConst.java +++ b/client/src/main/java/info/frostfs/sdk/constants/CryptoConst.java @@ -2,4 +2,7 @@ package info.frostfs.sdk.constants; public class CryptoConst { public static final String SIGNATURE_ALGORITHM = "NONEwithECDSAinP1363Format"; + + private CryptoConst() { + } } diff --git a/client/src/main/java/info/frostfs/sdk/jdo/ClientEnvironment.java b/client/src/main/java/info/frostfs/sdk/jdo/ClientEnvironment.java index 2165f06..6b140d3 100644 --- a/client/src/main/java/info/frostfs/sdk/jdo/ClientEnvironment.java +++ b/client/src/main/java/info/frostfs/sdk/jdo/ClientEnvironment.java @@ -2,8 +2,11 @@ package info.frostfs.sdk.jdo; import info.frostfs.sdk.dto.OwnerId; import info.frostfs.sdk.dto.Version; -import info.frostfs.sdk.services.FrostFSClient; +import info.frostfs.sdk.FrostFSClient; import io.grpc.Channel; +import org.apache.commons.lang3.StringUtils; + +import static java.util.Objects.isNull; public class ClientEnvironment { private final OwnerId ownerId; @@ -14,8 +17,12 @@ public class ClientEnvironment { private NetworkSettings networkSettings; public ClientEnvironment(String wif, Channel channel, Version version, FrostFSClient frostFSClient) { + if (StringUtils.isEmpty(wif) || isNull(channel) || isNull(version) || isNull(frostFSClient)) { + throw new IllegalArgumentException("One of the input attributes is missing"); + } + this.key = new ECDsa(wif); - this.ownerId = OwnerId.fromKey(key.getPublicKeyByte()); + this.ownerId = new OwnerId(key.getPublicKeyByte()); this.version = version; this.channel = channel; this.frostFSClient = frostFSClient; diff --git a/client/src/main/java/info/frostfs/sdk/jdo/ClientSettings.java b/client/src/main/java/info/frostfs/sdk/jdo/ClientSettings.java index 03b83ad..5f7bd32 100644 --- a/client/src/main/java/info/frostfs/sdk/jdo/ClientSettings.java +++ b/client/src/main/java/info/frostfs/sdk/jdo/ClientSettings.java @@ -13,6 +13,14 @@ public class ClientSettings { public ClientSettings(String key, String host) { this.key = key; this.host = host; + validate(); + } + + public ClientSettings(String key, String host, ChannelCredentials creds) { + this.key = key; + this.host = host; + this.creds = creds; + validate(); } public ChannelCredentials getCreds() { diff --git a/client/src/main/java/info/frostfs/sdk/jdo/ECDsa.java b/client/src/main/java/info/frostfs/sdk/jdo/ECDsa.java index 060eef9..d328c10 100644 --- a/client/src/main/java/info/frostfs/sdk/jdo/ECDsa.java +++ b/client/src/main/java/info/frostfs/sdk/jdo/ECDsa.java @@ -1,5 +1,7 @@ package info.frostfs.sdk.jdo; +import org.apache.commons.lang3.StringUtils; + import java.security.PrivateKey; import static info.frostfs.sdk.KeyExtension.*; @@ -10,6 +12,10 @@ public class ECDsa { private final PrivateKey privateKey; public ECDsa(String wif) { + if (StringUtils.isEmpty(wif)) { + throw new IllegalArgumentException("Wif is invalid"); + } + this.privateKeyByte = getPrivateKeyFromWIF(wif); this.publicKeyByte = loadPublicKey(privateKeyByte); this.privateKey = loadPrivateKey(privateKeyByte); diff --git a/client/src/main/java/info/frostfs/sdk/jdo/PutObjectParameters.java b/client/src/main/java/info/frostfs/sdk/jdo/PutObjectParameters.java index cd3338d..3c4ecdd 100644 --- a/client/src/main/java/info/frostfs/sdk/jdo/PutObjectParameters.java +++ b/client/src/main/java/info/frostfs/sdk/jdo/PutObjectParameters.java @@ -19,11 +19,15 @@ public class PutObjectParameters { this.payload = payload; this.clientCut = clientCut; this.bufferMaxSize = bufferMaxSize; + + validate(); } public PutObjectParameters(ObjectHeader header, FileInputStream payload) { this.header = header; this.payload = payload; + + validate(); } public ObjectHeader getHeader() { diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/ContainerService.java b/client/src/main/java/info/frostfs/sdk/services/impl/ContainerClientImpl.java similarity index 58% rename from client/src/main/java/info/frostfs/sdk/services/impl/ContainerService.java rename to client/src/main/java/info/frostfs/sdk/services/impl/ContainerClientImpl.java index 1e2f5e7..5c0e7af 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/ContainerService.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/ContainerClientImpl.java @@ -11,33 +11,36 @@ import info.frostfs.sdk.mappers.container.ContainerIdMapper; import info.frostfs.sdk.mappers.container.ContainerMapper; import info.frostfs.sdk.services.ContainerClient; import info.frostfs.sdk.services.ContextAccessor; -import info.frostfs.sdk.tools.Verifier; import info.frostfs.sdk.tools.RequestConstructor; import info.frostfs.sdk.tools.RequestSigner; +import info.frostfs.sdk.tools.Verifier; import java.util.List; import java.util.stream.Collectors; -import static info.frostfs.sdk.tools.RequestConstructor.addMetaHeader; -import static info.frostfs.sdk.tools.RequestSigner.signRFC6979; +import static java.util.Objects.isNull; -public class ContainerService extends ContextAccessor implements ContainerClient { +public class ContainerClientImpl extends ContextAccessor implements ContainerClient { private final ContainerServiceGrpc.ContainerServiceBlockingStub serviceBlockingStub; - public ContainerService(ClientEnvironment clientEnvironment) { + public ContainerClientImpl(ClientEnvironment clientEnvironment) { super(clientEnvironment); this.serviceBlockingStub = ContainerServiceGrpc.newBlockingStub(clientEnvironment.getChannel()); } + @Override public Container getContainer(ContainerId cid) { - var request = Service.GetRequest.newBuilder() - .setBody( - Service.GetRequest.Body.newBuilder() - .setContainerId(ContainerIdMapper.toGrpcMessage(cid)) - .build() - ); + if (isNull(cid)) { + throw new IllegalArgumentException("ContainerId is not present"); + } - RequestConstructor.addMetaHeader(request); + var body = Service.GetRequest.Body.newBuilder() + .setContainerId(ContainerIdMapper.toGrpcMessage(cid)) + .build(); + var request = Service.GetRequest.newBuilder() + .setBody(body); + + RequestConstructor.addDefaultMetaHeader(request); RequestSigner.sign(request, getContext().getKey()); var response = serviceBlockingStub.get(request.build()); @@ -46,15 +49,15 @@ public class ContainerService extends ContextAccessor implements ContainerClient return ContainerMapper.toModel(response.getBody().getContainer()); } + @Override public List listContainers() { + var body = Service.ListRequest.Body.newBuilder() + .setOwnerId(OwnerIdMapper.toGrpcMessage(getContext().getOwnerId())) + .build(); var request = Service.ListRequest.newBuilder() - .setBody( - Service.ListRequest.Body.newBuilder() - .setOwnerId(OwnerIdMapper.toGrpcMessage(getContext().getOwnerId())) - .build() - ); + .setBody(body); - RequestConstructor.addMetaHeader(request); + RequestConstructor.addDefaultMetaHeader(request); RequestSigner.sign(request, getContext().getKey()); var response = serviceBlockingStub.list(request.build()); @@ -62,51 +65,54 @@ public class ContainerService extends ContextAccessor implements ContainerClient Verifier.checkResponse(response); return response.getBody().getContainerIdsList().stream() - .map(cid -> ContainerId.fromHash(cid.getValue().toByteArray())) + .map(cid -> new ContainerId(cid.getValue().toByteArray())) .collect(Collectors.toList()); } + @Override public ContainerId createContainer(Container container) { - var grpcContainer = ContainerMapper.toGrpcMessage(container); + if (isNull(container)) { + throw new IllegalArgumentException("Container is not present"); + } + var grpcContainer = ContainerMapper.toGrpcMessage(container); grpcContainer = grpcContainer.toBuilder() .setOwnerId(OwnerIdMapper.toGrpcMessage(getContext().getOwnerId())) .setVersion(VersionMapper.toGrpcMessage(getContext().getVersion())) .build(); + var body = Service.PutRequest.Body.newBuilder() + .setContainer(grpcContainer) + .setSignature(RequestSigner.signRFC6979(getContext().getKey(), grpcContainer)) + .build(); var request = Service.PutRequest.newBuilder() - .setBody( - Service.PutRequest.Body.newBuilder() - .setContainer(grpcContainer) - .setSignature( - RequestSigner.signRFC6979(getContext().getKey(), grpcContainer) - ) - .build() - ); + .setBody(body); - RequestConstructor.addMetaHeader(request); + RequestConstructor.addDefaultMetaHeader(request); RequestSigner.sign(request, getContext().getKey()); var response = serviceBlockingStub.put(request.build()); Verifier.checkResponse(response); - return ContainerId.fromHash(response.getBody().getContainerId().getValue().toByteArray()); + return new ContainerId(response.getBody().getContainerId().getValue().toByteArray()); } + @Override public void deleteContainer(ContainerId cid) { + if (isNull(cid)) { + throw new IllegalArgumentException("ContainerId is not present"); + } + var grpcContainerId = ContainerIdMapper.toGrpcMessage(cid); + var body = Service.DeleteRequest.Body.newBuilder() + .setContainerId(grpcContainerId) + .setSignature(RequestSigner.signRFC6979(getContext().getKey(), grpcContainerId.getValue())) + .build(); var request = Service.DeleteRequest.newBuilder() - .setBody( - Service.DeleteRequest.Body.newBuilder() - .setContainerId(grpcContainerId) - .setSignature(RequestSigner.signRFC6979( - getContext().getKey(), grpcContainerId.getValue() - )) - .build() - ); + .setBody(body); - RequestConstructor.addMetaHeader(request); + RequestConstructor.addDefaultMetaHeader(request); RequestSigner.sign(request, getContext().getKey()); var response = serviceBlockingStub.delete(request.build()); diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/NetmapService.java b/client/src/main/java/info/frostfs/sdk/services/impl/NetmapClientImpl.java similarity index 91% rename from client/src/main/java/info/frostfs/sdk/services/impl/NetmapService.java rename to client/src/main/java/info/frostfs/sdk/services/impl/NetmapClientImpl.java index 19c5995..b38624c 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/NetmapService.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/NetmapClientImpl.java @@ -11,25 +11,27 @@ import info.frostfs.sdk.mappers.netmap.NetmapSnapshotMapper; import info.frostfs.sdk.mappers.netmap.NodeInfoMapper; import info.frostfs.sdk.services.ContextAccessor; import info.frostfs.sdk.services.NetmapClient; +import info.frostfs.sdk.tools.RequestConstructor; import info.frostfs.sdk.tools.Verifier; import java.nio.charset.StandardCharsets; -import static info.frostfs.sdk.tools.RequestConstructor.addMetaHeader; import static info.frostfs.sdk.tools.RequestSigner.sign; import static java.util.Objects.nonNull; -public class NetmapService extends ContextAccessor implements NetmapClient { +public class NetmapClientImpl extends ContextAccessor implements NetmapClient { private final NetmapServiceGrpc.NetmapServiceBlockingStub netmapServiceClient; - public NetmapService(ClientEnvironment clientEnvironment) { + public NetmapClientImpl(ClientEnvironment clientEnvironment) { super(clientEnvironment); this.netmapServiceClient = NetmapServiceGrpc.newBlockingStub(getContext().getChannel()); } private static boolean getBoolValue(byte[] bytes) { for (var byteValue : bytes) { - if (byteValue != 0) return true; + if (byteValue != 0) { + return true; + } } return false; @@ -115,7 +117,7 @@ public class NetmapService extends ContextAccessor implements NetmapClient { var request = Service.LocalNodeInfoRequest.newBuilder() .setBody(Service.LocalNodeInfoRequest.Body.newBuilder().build()); - addMetaHeader(request); + RequestConstructor.addDefaultMetaHeader(request); sign(request, getContext().getKey()); var response = netmapServiceClient.localNodeInfo(request.build()); @@ -128,7 +130,7 @@ public class NetmapService extends ContextAccessor implements NetmapClient { var request = Service.NetworkInfoRequest.newBuilder() .setBody(Service.NetworkInfoRequest.Body.newBuilder().build()); - addMetaHeader(request); + RequestConstructor.addDefaultMetaHeader(request); sign(request, getContext().getKey()); var response = netmapServiceClient.networkInfo(request.build()); @@ -143,7 +145,7 @@ public class NetmapService extends ContextAccessor implements NetmapClient { var request = Service.NetmapSnapshotRequest.newBuilder() .setBody(Service.NetmapSnapshotRequest.Body.newBuilder().build()); - addMetaHeader(request); + RequestConstructor.addDefaultMetaHeader(request); sign(request, getContext().getKey()); var response = netmapServiceClient.netmapSnapshot(request.build()); diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/ObjectService.java b/client/src/main/java/info/frostfs/sdk/services/impl/ObjectClientImpl.java similarity index 81% rename from client/src/main/java/info/frostfs/sdk/services/impl/ObjectService.java rename to client/src/main/java/info/frostfs/sdk/services/impl/ObjectClientImpl.java index ca2652b..69046d6 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/ObjectService.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/ObjectClientImpl.java @@ -20,6 +20,10 @@ import info.frostfs.sdk.mappers.object.ObjectHeaderMapper; import info.frostfs.sdk.mappers.object.ObjectIdMapper; import info.frostfs.sdk.services.ContextAccessor; import info.frostfs.sdk.services.ObjectClient; +import info.frostfs.sdk.services.impl.rwhelper.ObjectReader; +import info.frostfs.sdk.services.impl.rwhelper.ObjectWriter; +import info.frostfs.sdk.services.impl.rwhelper.SearchReader; +import info.frostfs.sdk.tools.RequestConstructor; import info.frostfs.sdk.tools.Verifier; import org.apache.commons.collections4.CollectionUtils; @@ -30,39 +34,37 @@ import java.util.Arrays; import java.util.List; import static info.frostfs.sdk.Helper.getSha256; -import static info.frostfs.sdk.tools.RequestConstructor.addMetaHeader; import static info.frostfs.sdk.tools.RequestConstructor.addObjectSessionToken; import static info.frostfs.sdk.tools.RequestSigner.sign; import static java.util.Objects.nonNull; -public class ObjectService extends ContextAccessor implements ObjectClient { +public class ObjectClientImpl extends ContextAccessor implements ObjectClient { private static final String ERROR_PAYLOAD = "PayloadLength must be specified"; private final ObjectServiceGrpc.ObjectServiceBlockingStub objectServiceBlockingClient; private final ObjectServiceGrpc.ObjectServiceStub objectServiceClient; - private final ObjectTools objectTools; + private final ObjectToolsImpl objectToolsImpl; - public ObjectService(ClientEnvironment clientEnvironment) { + public ObjectClientImpl(ClientEnvironment clientEnvironment) { super(clientEnvironment); this.objectServiceBlockingClient = ObjectServiceGrpc.newBlockingStub(getContext().getChannel()); this.objectServiceClient = ObjectServiceGrpc.newStub(getContext().getChannel()); - this.objectTools = new ObjectTools(clientEnvironment); + this.objectToolsImpl = new ObjectToolsImpl(clientEnvironment); } @Override public ObjectHeader getObjectHead(ContainerId cid, ObjectId oid) { + var address = Types.Address.newBuilder() + .setContainerId(ContainerIdMapper.toGrpcMessage(cid)) + .setObjectId(ObjectIdMapper.toGrpcMessage(oid)) + .build(); + var body = Service.HeadRequest.Body.newBuilder() + .setAddress(address) + .build(); var request = Service.HeadRequest.newBuilder() - .setBody( - Service.HeadRequest.Body.newBuilder() - .setAddress( - Types.Address.newBuilder() - .setContainerId(ContainerIdMapper.toGrpcMessage(cid)) - .setObjectId(ObjectIdMapper.toGrpcMessage(oid)) - .build() - ).build() - ); + .setBody(body); - addMetaHeader(request); + RequestConstructor.addDefaultMetaHeader(request); sign(request, getContext().getKey()); var response = objectServiceBlockingClient.head(request.build()); @@ -75,19 +77,17 @@ public class ObjectService extends ContextAccessor implements ObjectClient { public ObjectFrostFS getObject(ContainerId cid, ObjectId oid) { var sessionToken = getContext().getFrostFSClient().createSessionInternal(-1); + var address = Types.Address.newBuilder() + .setContainerId(ContainerIdMapper.toGrpcMessage(cid)) + .setObjectId(ObjectIdMapper.toGrpcMessage(oid)) + .build(); + var body = Service.GetRequest.Body.newBuilder() + .setAddress(address) + .build(); var request = Service.GetRequest.newBuilder() - .setBody( - Service.GetRequest.Body.newBuilder() - .setAddress( - Types.Address.newBuilder() - .setContainerId(ContainerIdMapper.toGrpcMessage(cid)) - .setObjectId(ObjectIdMapper.toGrpcMessage(oid)) - .build() - ) - .build() - ); + .setBody(body); - addMetaHeader(request); + RequestConstructor.addDefaultMetaHeader(request); addObjectSessionToken( request, sessionToken, ContainerIdMapper.toGrpcMessage(cid), ObjectIdMapper.toGrpcMessage(oid), frostfs.session.Types.ObjectSessionContext.Verb.GET, getContext().getKey() @@ -101,18 +101,17 @@ public class ObjectService extends ContextAccessor implements ObjectClient { @Override public void deleteObject(ContainerId cid, ObjectId oid) { + var address = Types.Address.newBuilder() + .setContainerId(ContainerIdMapper.toGrpcMessage(cid)) + .setObjectId(ObjectIdMapper.toGrpcMessage(oid)) + .build(); + var body = Service.DeleteRequest.Body.newBuilder() + .setAddress(address) + .build(); var request = Service.DeleteRequest.newBuilder() - .setBody( - Service.DeleteRequest.Body.newBuilder() - .setAddress( - Types.Address.newBuilder() - .setContainerId(ContainerIdMapper.toGrpcMessage(cid)) - .setObjectId(ObjectIdMapper.toGrpcMessage(oid)) - .build() - ) - .build()); + .setBody(body); - addMetaHeader(request); + RequestConstructor.addDefaultMetaHeader(request); sign(request, getContext().getKey()); var response = objectServiceBlockingClient.delete(request.build()); @@ -132,12 +131,12 @@ public class ObjectService extends ContextAccessor implements ObjectClient { var request = Service.SearchRequest.newBuilder() .setBody(body.build()); - addMetaHeader(request); + RequestConstructor.addDefaultMetaHeader(request); sign(request, getContext().getKey()); var objectsIds = searchObjects(request.build()); - return Iterables.transform(objectsIds, input -> ObjectId.fromHash(input.getValue().toByteArray())); + return Iterables.transform(objectsIds, input -> new ObjectId(input.getValue().toByteArray())); } @Override @@ -150,13 +149,13 @@ public class ObjectService extends ContextAccessor implements ObjectClient { public ObjectId putSingleObject(ObjectFrostFS modelObject) { var sessionToken = getContext().getFrostFSClient().createSessionInternal(-1); - var grpcObject = objectTools.createObject(modelObject); + var grpcObject = objectToolsImpl.createObject(modelObject); var request = Service.PutSingleRequest.newBuilder() .setBody(Service.PutSingleRequest.Body.newBuilder().setObject(grpcObject).build()); - addMetaHeader(request); + RequestConstructor.addDefaultMetaHeader(request); addObjectSessionToken( request, sessionToken, grpcObject.getHeader().getContainerId(), grpcObject.getObjectId(), frostfs.session.Types.ObjectSessionContext.Verb.PUT, getContext().getKey() @@ -167,7 +166,7 @@ public class ObjectService extends ContextAccessor implements ObjectClient { Verifier.checkResponse(response); - return ObjectId.fromHash(grpcObject.getObjectId().getValue().toByteArray()); + return new ObjectId(grpcObject.getObjectId().getValue().toByteArray()); } private frostfs.object.Types.Object getObject(Service.GetRequest request) { @@ -215,7 +214,7 @@ public class ObjectService extends ContextAccessor implements ObjectClient { ).build() ); - addMetaHeader(initRequest); + RequestConstructor.addDefaultMetaHeader(initRequest); addObjectSessionToken( initRequest, sessionToken, hdr.getContainerId(), oid, frostfs.session.Types.ObjectSessionContext.Verb.PUT, getContext().getKey() @@ -232,8 +231,9 @@ public class ObjectService extends ContextAccessor implements ObjectClient { var buffer = new byte[bufferSize]; while (true) { var bytesCount = readNBytes(parameters.getPayload(), buffer, bufferSize); - if (bytesCount <= 0) + if (bytesCount <= 0) { break; + } var chunkRequest = Service.PutRequest.newBuilder(initRequest.build()) .setBody( @@ -249,7 +249,7 @@ public class ObjectService extends ContextAccessor implements ObjectClient { var response = writer.complete(); Verifier.checkResponse(response); - return ObjectId.fromHash(response.getBody().getObjectId().getValue().toByteArray()); + return new ObjectId(response.getBody().getObjectId().getValue().toByteArray()); } private ObjectId putClientCutObject(PutObjectParameters parameters) { @@ -285,8 +285,9 @@ public class ObjectService extends ContextAccessor implements ObjectClient { ); currentObject.setSplit(split); - if (largeObject.getPayloadLength() == fullLength) + if (largeObject.getPayloadLength() == fullLength) { break; + } objectId = putSingleObject(currentObject); @@ -314,7 +315,7 @@ public class ObjectService extends ContextAccessor implements ObjectClient { putSingleObject(linkObject); - return objectTools.calculateObjectId(largeObject.getHeader()); + return objectToolsImpl.calculateObjectId(largeObject.getHeader()); } private ObjectWriter putObjectInit(Service.PutRequest initRequest) { diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/ObjectTools.java b/client/src/main/java/info/frostfs/sdk/services/impl/ObjectToolsImpl.java similarity index 79% rename from client/src/main/java/info/frostfs/sdk/services/impl/ObjectTools.java rename to client/src/main/java/info/frostfs/sdk/services/impl/ObjectToolsImpl.java index 5eb0680..b68a26e 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/ObjectTools.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/ObjectToolsImpl.java @@ -18,8 +18,8 @@ import static info.frostfs.sdk.Helper.getSha256; import static info.frostfs.sdk.tools.RequestSigner.signData; import static java.util.Objects.nonNull; -public class ObjectTools extends ContextAccessor implements ToolsClient { - public ObjectTools(ClientEnvironment context) { +public class ObjectToolsImpl extends ContextAccessor implements ToolsClient { + public ObjectToolsImpl(ClientEnvironment context) { super(context); } @@ -59,15 +59,13 @@ public class ObjectTools extends ContextAccessor implements ToolsClient { if (nonNull(split.getParentHeader())) { var grpcParentHeader = createHeader(split.getParentHeader(), new byte[]{}); var parent = frostfs.refs.Types.ObjectID.newBuilder().setValue(getSha256(grpcParentHeader)).build(); + var parentSig = frostfs.refs.Types.Signature.newBuilder() + .setKey(ByteString.copyFrom(getContext().getKey().getPublicKeyByte())) + .setSign(ByteString.copyFrom(signData(getContext().getKey(), parent.toByteArray()))); - splitGrpc - .setParent(parent) + splitGrpc.setParent(parent) .setParentHeader(grpcParentHeader) - .setParentSignature( - frostfs.refs.Types.Signature.newBuilder() - .setKey(ByteString.copyFrom(getContext().getKey().getPublicKeyByte())) - .setSign(ByteString.copyFrom(signData(getContext().getKey(), parent.toByteArray()))) - ); + .setParentSignature(parentSig); split.setParent(ObjectIdMapper.toModel(parent)); } @@ -79,15 +77,14 @@ public class ObjectTools extends ContextAccessor implements ToolsClient { var grpcHeader = grpcHeaderBuilder.build(); var objectId = frostfs.refs.Types.ObjectID.newBuilder().setValue(getSha256(grpcHeader)).build(); + var sig = frostfs.refs.Types.Signature.newBuilder() + .setKey(ByteString.copyFrom(getContext().getKey().getPublicKeyByte())) + .setSign(ByteString.copyFrom(signData(getContext().getKey(), objectId.toByteArray()))); return Types.Object.newBuilder() .setHeader(grpcHeader) .setObjectId(objectId) .setPayload(ByteString.copyFrom(objectFrostFs.getPayload())) - .setSignature( - frostfs.refs.Types.Signature.newBuilder() - .setKey(ByteString.copyFrom(getContext().getKey().getPublicKeyByte())) - .setSign(ByteString.copyFrom(signData(getContext().getKey(), objectId.toByteArray()))) - ) + .setSignature(sig) .build(); } @@ -96,10 +93,11 @@ public class ObjectTools extends ContextAccessor implements ToolsClient { .setOwnerId(OwnerIdMapper.toGrpcMessage(getContext().getOwnerId())) .setVersion(VersionMapper.toGrpcMessage(getContext().getVersion())); - if (header.getPayloadCheckSum() != null) + if (header.getPayloadCheckSum() != null) { grpcHeader.setPayloadHash(sha256Checksum(header.getPayloadCheckSum())); - else if (payload != null) + } else if (payload != null) { grpcHeader.setPayloadHash(sha256Checksum(payload)); + } return grpcHeader.build(); } diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/SessionService.java b/client/src/main/java/info/frostfs/sdk/services/impl/SessionClientImpl.java similarity index 79% rename from client/src/main/java/info/frostfs/sdk/services/impl/SessionService.java rename to client/src/main/java/info/frostfs/sdk/services/impl/SessionClientImpl.java index d6bf950..137c178 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/SessionService.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/SessionClientImpl.java @@ -11,13 +11,12 @@ import info.frostfs.sdk.services.ContextAccessor; import info.frostfs.sdk.services.SessionClient; import info.frostfs.sdk.tools.RequestConstructor; -import static info.frostfs.sdk.tools.RequestConstructor.addMetaHeader; import static info.frostfs.sdk.tools.RequestSigner.sign; -public class SessionService extends ContextAccessor implements SessionClient { +public class SessionClientImpl extends ContextAccessor implements SessionClient { private final SessionServiceGrpc.SessionServiceBlockingStub serviceBlockingStub; - public SessionService(ClientEnvironment clientEnvironment) { + public SessionClientImpl(ClientEnvironment clientEnvironment) { super(clientEnvironment); this.serviceBlockingStub = SessionServiceGrpc.newBlockingStub(getContext().getChannel()); } @@ -30,14 +29,14 @@ public class SessionService extends ContextAccessor implements SessionClient { } public Types.SessionToken createSessionInternal(long expiration) { + var body = Service.CreateRequest.Body.newBuilder() + .setOwnerId(OwnerIdMapper.toGrpcMessage(getContext().getOwnerId())) + .setExpiration(expiration) + .build(); var request = Service.CreateRequest.newBuilder() - .setBody( - Service.CreateRequest.Body.newBuilder() - .setOwnerId(OwnerIdMapper.toGrpcMessage(getContext().getOwnerId())) - .setExpiration(expiration).build() - ); + .setBody(body); - RequestConstructor.addMetaHeader(request); + RequestConstructor.addDefaultMetaHeader(request); sign(request, getContext().getKey()); return createSession(request.build()); diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/ObjectReader.java b/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectReader.java similarity index 96% rename from client/src/main/java/info/frostfs/sdk/services/impl/ObjectReader.java rename to client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectReader.java index 62a9a70..d686921 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/ObjectReader.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectReader.java @@ -1,4 +1,4 @@ -package info.frostfs.sdk.services.impl; +package info.frostfs.sdk.services.impl.rwhelper; import frostfs.object.Service; import frostfs.object.Types; diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/ObjectWriter.java b/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectWriter.java similarity index 97% rename from client/src/main/java/info/frostfs/sdk/services/impl/ObjectWriter.java rename to client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectWriter.java index 8642dc8..7d6271b 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/ObjectWriter.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectWriter.java @@ -1,4 +1,4 @@ -package info.frostfs.sdk.services.impl; +package info.frostfs.sdk.services.impl.rwhelper; import frostfs.object.ObjectServiceGrpc; import frostfs.object.Service; diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/SearchReader.java b/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/SearchReader.java similarity index 91% rename from client/src/main/java/info/frostfs/sdk/services/impl/SearchReader.java rename to client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/SearchReader.java index 873d48b..ec42c05 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/SearchReader.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/SearchReader.java @@ -1,4 +1,4 @@ -package info.frostfs.sdk.services.impl; +package info.frostfs.sdk.services.impl.rwhelper; import frostfs.object.Service; import info.frostfs.sdk.tools.Verifier; diff --git a/client/src/main/java/info/frostfs/sdk/tools/GrpcClient.java b/client/src/main/java/info/frostfs/sdk/tools/GrpcClient.java index 729d3e5..96ef9b2 100644 --- a/client/src/main/java/info/frostfs/sdk/tools/GrpcClient.java +++ b/client/src/main/java/info/frostfs/sdk/tools/GrpcClient.java @@ -12,6 +12,9 @@ import static java.util.Objects.isNull; public class GrpcClient { private static final String ERROR_INVALID_HOST_TEMPLATE = "Host %s has invalid format. Error: %s"; + private GrpcClient() { + } + public static Channel initGrpcChannel(String host, ChannelCredentials creds) { try { URI uri = new URI(host); diff --git a/client/src/main/java/info/frostfs/sdk/tools/MessageHelper.java b/client/src/main/java/info/frostfs/sdk/tools/MessageHelper.java index 00f87c4..cb0005f 100644 --- a/client/src/main/java/info/frostfs/sdk/tools/MessageHelper.java +++ b/client/src/main/java/info/frostfs/sdk/tools/MessageHelper.java @@ -5,6 +5,9 @@ import com.google.protobuf.MessageOrBuilder; public class MessageHelper { + private MessageHelper() { + } + public static Message getField(MessageOrBuilder messageOrBuilder, String fieldName) { return (Message) messageOrBuilder.getField(messageOrBuilder.getDescriptorForType().findFieldByName(fieldName)); } diff --git a/client/src/main/java/info/frostfs/sdk/tools/RequestConstructor.java b/client/src/main/java/info/frostfs/sdk/tools/RequestConstructor.java index 9660de5..047d146 100644 --- a/client/src/main/java/info/frostfs/sdk/tools/RequestConstructor.java +++ b/client/src/main/java/info/frostfs/sdk/tools/RequestConstructor.java @@ -14,13 +14,20 @@ import static java.util.Objects.isNull; public class RequestConstructor { - public static void addMetaHeader(Message.Builder request) { + private RequestConstructor() { + } + + public static void addDefaultMetaHeader(Message.Builder request) { addMetaHeader(request, null); } public static void addMetaHeader(Message.Builder request, Types.RequestMetaHeader metaHeader) { + if (isNull(request)) { + return; + } + if (isNull(metaHeader) || metaHeader.getSerializedSize() == 0) { - metaHeader = MetaHeaderMapper.toGrpcMessage(MetaHeader.getDefault()); + metaHeader = MetaHeaderMapper.toGrpcMessage(new MetaHeader()); setField(request, META_HEADER_FIELD_NAME, metaHeader); } } @@ -31,6 +38,10 @@ public class RequestConstructor { frostfs.refs.Types.ObjectID oid, Types.ObjectSessionContext.Verb verb, ECDsa key) { + if (isNull(request) || isNull(sessionToken)) { + return; + } + var header = (Types.RequestMetaHeader) getField(request, META_HEADER_FIELD_NAME); if (header.getSessionToken().getSerializedSize() > 0) { return; diff --git a/client/src/main/java/info/frostfs/sdk/tools/RequestSigner.java b/client/src/main/java/info/frostfs/sdk/tools/RequestSigner.java index 315af58..ea7d828 100644 --- a/client/src/main/java/info/frostfs/sdk/tools/RequestSigner.java +++ b/client/src/main/java/info/frostfs/sdk/tools/RequestSigner.java @@ -3,8 +3,8 @@ package info.frostfs.sdk.tools; import com.google.protobuf.ByteString; import com.google.protobuf.Message; import frostfs.session.Types; -import info.frostfs.sdk.jdo.ECDsa; import info.frostfs.sdk.constants.CryptoConst; +import info.frostfs.sdk.jdo.ECDsa; import org.apache.commons.codec.digest.DigestUtils; import org.bouncycastle.asn1.sec.SECNamedCurves; import org.bouncycastle.asn1.sec.SECObjectIdentifiers; @@ -24,6 +24,9 @@ public class RequestSigner { public static final String ERROR_UNSUPPORTED_TYPE_TEMPLATE = "Unsupported message type: %s"; public static final int RFC6979_SIGNATURE_SIZE = 64; + private RequestSigner() { + } + public static byte[] signData(ECDsa key, byte[] data) { var hash = new byte[65]; hash[0] = 0x04; diff --git a/client/src/main/java/info/frostfs/sdk/tools/Verifier.java b/client/src/main/java/info/frostfs/sdk/tools/Verifier.java index 8fed127..48a2afc 100644 --- a/client/src/main/java/info/frostfs/sdk/tools/Verifier.java +++ b/client/src/main/java/info/frostfs/sdk/tools/Verifier.java @@ -2,8 +2,8 @@ package info.frostfs.sdk.tools; import com.google.protobuf.Message; import frostfs.session.Types; -import info.frostfs.sdk.mappers.StatusMapper; import info.frostfs.sdk.constants.CryptoConst; +import info.frostfs.sdk.mappers.StatusMapper; import org.apache.commons.codec.digest.DigestUtils; import org.bouncycastle.asn1.sec.SECNamedCurves; import org.bouncycastle.asn1.sec.SECObjectIdentifiers; @@ -28,12 +28,17 @@ public class Verifier { public static final String ERROR_INVALID_RESPONSE = "Invalid response"; public static final int RFC6979_SIG_SIZE = 64; + private Verifier() { + } + public static boolean verifyRFC6979(frostfs.refs.Types.SignatureRFC6979 signature, Message data) { return verifyRFC6979(signature.getKey().toByteArray(), data.toByteArray(), signature.getSign().toByteArray()); } public static boolean verifyRFC6979(byte[] publicKey, byte[] data, byte[] sig) { - if (isNull(publicKey) || isNull(data) || isNull(sig)) return false; + if (isNull(publicKey) || isNull(data) || isNull(sig)) { + return false; + } var rs = decodeSignature(sig); var digest = createSHA256(); @@ -85,9 +90,15 @@ public class Verifier { public static boolean verifyMatryoshkaLevel(Message data, frostfs.session.Types.ResponseMetaHeader meta, frostfs.session.Types.ResponseVerificationHeader verification) { - if (!verifyMessagePart(verification.getMetaSignature(), meta)) return false; + if (!verifyMessagePart(verification.getMetaSignature(), meta)) { + return false; + } + var origin = verification.getOrigin(); - if (!verifyMessagePart(verification.getOriginSignature(), origin)) return false; + if (!verifyMessagePart(verification.getOriginSignature(), origin)) { + return false; + } + if (origin.getSerializedSize() == 0) { return verifyMessagePart(verification.getBodySignature(), data); } @@ -96,7 +107,9 @@ public class Verifier { } public static boolean verifyMessagePart(frostfs.refs.Types.Signature sig, Message data) { - if (sig.getSerializedSize() == 0 || sig.getKey().isEmpty() || sig.getSign().isEmpty()) return false; + if (sig.getSerializedSize() == 0 || sig.getKey().isEmpty() || sig.getSign().isEmpty()) { + return false; + } var publicKey = getPublicKeyFromBytes(sig.getKey().toByteArray()); diff --git a/cryptography/pom.xml b/cryptography/pom.xml index 156b0bd..385a810 100644 --- a/cryptography/pom.xml +++ b/cryptography/pom.xml @@ -29,6 +29,11 @@ bcprov-jdk18on 1.78.1 + + org.apache.commons + commons-lang3 + 3.14.0 + \ No newline at end of file diff --git a/cryptography/src/main/java/info/frostfs/sdk/ArrayHelper.java b/cryptography/src/main/java/info/frostfs/sdk/ArrayHelper.java index ae43afd..915f202 100644 --- a/cryptography/src/main/java/info/frostfs/sdk/ArrayHelper.java +++ b/cryptography/src/main/java/info/frostfs/sdk/ArrayHelper.java @@ -1,6 +1,8 @@ package info.frostfs.sdk; public class ArrayHelper { + private ArrayHelper() { + } public static byte[] concat(byte[] startArray, byte[] endArray) { byte[] result = new byte[startArray.length + endArray.length]; diff --git a/cryptography/src/main/java/info/frostfs/sdk/Base58.java b/cryptography/src/main/java/info/frostfs/sdk/Base58.java index f75d705..6321d58 100644 --- a/cryptography/src/main/java/info/frostfs/sdk/Base58.java +++ b/cryptography/src/main/java/info/frostfs/sdk/Base58.java @@ -1,5 +1,7 @@ package info.frostfs.sdk; +import org.apache.commons.lang3.StringUtils; + import java.util.Arrays; import static info.frostfs.sdk.ArrayHelper.concat; @@ -18,8 +20,11 @@ public class Base58 { } } + private Base58() { + } + public static byte[] base58CheckDecode(String input) { - if (isNull(input) || input.isEmpty()) { + if (StringUtils.isEmpty(input)) { throw new IllegalArgumentException("Input value is missing"); } @@ -30,10 +35,9 @@ public class Base58 { byte[] decode = Arrays.copyOfRange(buffer, 0, buffer.length - 4); byte[] checksum = getSha256(getSha256(decode)); - if (!Arrays.equals( - Arrays.copyOfRange(buffer, buffer.length - 4, buffer.length), - Arrays.copyOfRange(checksum, 0, 4) - )) { + var bufferEnd = Arrays.copyOfRange(buffer, buffer.length - 4, buffer.length); + var checksumStart = Arrays.copyOfRange(checksum, 0, 4); + if (!Arrays.equals(bufferEnd, checksumStart)) { throw new IllegalArgumentException(); } @@ -41,6 +45,10 @@ public class Base58 { } public static String base58CheckEncode(byte[] data) { + if (isNull(data)) { + throw new IllegalArgumentException("Input value is missing"); + } + byte[] checksum = getSha256(getSha256(data)); var buffer = concat(data, Arrays.copyOfRange(checksum, 0, 4)); var ret = encode(buffer); diff --git a/cryptography/src/main/java/info/frostfs/sdk/Helper.java b/cryptography/src/main/java/info/frostfs/sdk/Helper.java index a6068e2..58b8c1e 100644 --- a/cryptography/src/main/java/info/frostfs/sdk/Helper.java +++ b/cryptography/src/main/java/info/frostfs/sdk/Helper.java @@ -8,9 +8,17 @@ import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import static java.util.Objects.isNull; + public class Helper { + private Helper() { + } public static byte[] getRIPEMD160(byte[] value) { + if (isNull(value)) { + throw new IllegalArgumentException("Input value is missing"); + } + var hash = new byte[20]; var digest = new RIPEMD160Digest(); digest.update(value, 0, value.length); @@ -27,14 +35,26 @@ public class Helper { } public static byte[] getSha256(byte[] value) { + if (isNull(value)) { + throw new IllegalArgumentException("Input value is missing"); + } + return getSha256Instance().digest(value); } public static ByteString getSha256(Message value) { + if (isNull(value)) { + throw new IllegalArgumentException("Input value is missing"); + } + return ByteString.copyFrom(getSha256(value.toByteArray())); } - public static String getHexString(byte[] array) { - return String.format("%0" + (array.length << 1) + "x", new BigInteger(1, array)); + public static String getHexString(byte[] value) { + if (isNull(value)) { + throw new IllegalArgumentException("Input value is missing"); + } + + return String.format("%0" + (value.length << 1) + "x", new BigInteger(1, value)); } } diff --git a/cryptography/src/main/java/info/frostfs/sdk/KeyExtension.java b/cryptography/src/main/java/info/frostfs/sdk/KeyExtension.java index 4ef8ffe..40affe5 100644 --- a/cryptography/src/main/java/info/frostfs/sdk/KeyExtension.java +++ b/cryptography/src/main/java/info/frostfs/sdk/KeyExtension.java @@ -1,5 +1,6 @@ package info.frostfs.sdk; +import org.apache.commons.lang3.StringUtils; import org.bouncycastle.asn1.sec.SECNamedCurves; import org.bouncycastle.asn1.sec.SECObjectIdentifiers; import org.bouncycastle.asn1.x9.X9ECParameters; @@ -25,19 +26,23 @@ import java.util.Arrays; import static info.frostfs.sdk.Helper.getRIPEMD160; import static info.frostfs.sdk.Helper.getSha256; +import static java.util.Objects.isNull; import static org.bouncycastle.util.BigIntegers.fromUnsignedByteArray; public class KeyExtension { public static final byte NEO_ADDRESS_VERSION = 0x35; + private static final int DECODE_ADDRESS_LENGTH = 21; private static final int COMPRESSED_PUBLIC_KEY_LENGTH = 33; private static final int UNCOMPRESSED_PUBLIC_KEY_LENGTH = 65; - private static final int CHECK_SIG_DESCRIPTOR = ByteBuffer .wrap(getSha256("System.Crypto.CheckSig".getBytes(StandardCharsets.US_ASCII))) .order(ByteOrder.LITTLE_ENDIAN).getInt(); + private KeyExtension() { + } public static byte[] compress(byte[] publicKey) { + checkInputValue(publicKey); if (publicKey.length != UNCOMPRESSED_PUBLIC_KEY_LENGTH) { throw new IllegalArgumentException( String.format("Compress argument isn't uncompressed public key. Expected length=%s, actual=%s", @@ -51,11 +56,17 @@ public class KeyExtension { } public static byte[] getPrivateKeyFromWIF(String wif) { + if (StringUtils.isEmpty(wif)) { + throw new IllegalArgumentException("Input value is missing"); + } + var data = Base58.base58CheckDecode(wif); return Arrays.copyOfRange(data, 1, data.length - 1); } public static byte[] loadPublicKey(byte[] privateKey) { + checkInputValue(privateKey); + X9ECParameters params = SECNamedCurves.getByOID(SECObjectIdentifiers.secp256r1); ECDomainParameters domain = new ECDomainParameters( params.getCurve(), params.getG(), params.getN(), params.getH() @@ -66,6 +77,8 @@ public class KeyExtension { } public static PrivateKey loadPrivateKey(byte[] privateKey) { + checkInputValue(privateKey); + X9ECParameters params = SECNamedCurves.getByOID(SECObjectIdentifiers.secp256r1); ECDomainParameters domain = new ECDomainParameters( params.getCurve(), params.getG(), params.getN(), params.getH() @@ -85,6 +98,8 @@ public class KeyExtension { } public static PublicKey getPublicKeyFromBytes(byte[] publicKey) { + checkInputValue(publicKey); + if (publicKey.length != COMPRESSED_PUBLIC_KEY_LENGTH) { throw new IllegalArgumentException( String.format("Decompress argument isn't compressed public key. Expected length=%s, actual=%s", @@ -116,12 +131,14 @@ public class KeyExtension { } public static byte[] getScriptHash(byte[] publicKey) { - var script = createSignatureRedeemScript(publicKey); + checkInputValue(publicKey); + var script = createSignatureRedeemScript(publicKey); return getRIPEMD160(getSha256(script)); } public static String publicKeyToAddress(byte[] publicKey) { + checkInputValue(publicKey); if (publicKey.length != COMPRESSED_PUBLIC_KEY_LENGTH) { throw new IllegalArgumentException( String.format("PublicKey isn't encoded compressed public key. Expected length=%s, actual=%s", @@ -133,7 +150,8 @@ public class KeyExtension { } private static String toAddress(byte[] scriptHash, byte version) { - byte[] data = new byte[21]; + checkInputValue(scriptHash); + byte[] data = new byte[DECODE_ADDRESS_LENGTH]; data[0] = version; System.arraycopy(scriptHash, 0, data, 1, scriptHash.length); return Base58.base58CheckEncode(data); @@ -150,6 +168,7 @@ public class KeyExtension { } private static byte[] createSignatureRedeemScript(byte[] publicKey) { + checkInputValue(publicKey); if (publicKey.length != COMPRESSED_PUBLIC_KEY_LENGTH) { throw new IllegalArgumentException( String.format("PublicKey isn't encoded compressed public key. Expected length=%s, actual=%s", @@ -164,4 +183,10 @@ public class KeyExtension { script = ArrayHelper.concat(script, getBytes(CHECK_SIG_DESCRIPTOR)); //Neo_Crypto_CheckSig return script; } + + private static void checkInputValue(byte[] data) { + if (isNull(data)) { + throw new IllegalArgumentException("Input value is missing"); + } + } } diff --git a/models/pom.xml b/models/pom.xml index e9dd655..edc1c26 100644 --- a/models/pom.xml +++ b/models/pom.xml @@ -18,11 +18,6 @@ - - org.apache.commons - commons-lang3 - 3.14.0 - info.frostfs.sdk cryptography @@ -33,6 +28,16 @@ protos 0.1.0 + + org.apache.commons + commons-lang3 + 3.14.0 + + + org.apache.commons + commons-collections4 + 4.4 + \ No newline at end of file diff --git a/models/src/main/java/info/frostfs/sdk/UUIDExtension.java b/models/src/main/java/info/frostfs/sdk/UUIDExtension.java index 7edc01c..730a1a5 100644 --- a/models/src/main/java/info/frostfs/sdk/UUIDExtension.java +++ b/models/src/main/java/info/frostfs/sdk/UUIDExtension.java @@ -3,19 +3,33 @@ package info.frostfs.sdk; import java.nio.ByteBuffer; import java.util.UUID; +import static java.util.Objects.isNull; + public class UUIDExtension { + private static final int UUID_BYTE_ARRAY_LENGTH = 16; + + private UUIDExtension() { + } public static UUID asUuid(byte[] bytes) { + if (isNull(bytes) || bytes.length != UUID_BYTE_ARRAY_LENGTH) { + throw new IllegalArgumentException("Uuid byte array length must be " + UUID_BYTE_ARRAY_LENGTH); + } + ByteBuffer bb = ByteBuffer.wrap(bytes); long firstLong = bb.getLong(); long secondLong = bb.getLong(); return new UUID(firstLong, secondLong); } - public static byte[] asBytes(UUID id) { + public static byte[] asBytes(UUID uuid) { + if (isNull(uuid)) { + throw new IllegalArgumentException("Uuid is not present"); + } + ByteBuffer bb = ByteBuffer.allocate(16); - bb.putLong(id.getMostSignificantBits()); - bb.putLong(id.getLeastSignificantBits()); + bb.putLong(uuid.getMostSignificantBits()); + bb.putLong(uuid.getLeastSignificantBits()); return bb.array(); } } diff --git a/models/src/main/java/info/frostfs/sdk/constants/AppConst.java b/models/src/main/java/info/frostfs/sdk/constants/AppConst.java index ea60f0e..a823466 100644 --- a/models/src/main/java/info/frostfs/sdk/constants/AppConst.java +++ b/models/src/main/java/info/frostfs/sdk/constants/AppConst.java @@ -1,6 +1,11 @@ package info.frostfs.sdk.constants; public class AppConst { + public static final int DEFAULT_MAJOR_VERSION = 2; + public static final int DEFAULT_MINOR_VERSION = 13; public static final int OBJECT_CHUNK_SIZE = 3 * (1 << 20); public static final int SHA256_HASH_LENGTH = 32; + + private AppConst() { + } } diff --git a/models/src/main/java/info/frostfs/sdk/constants/FieldConst.java b/models/src/main/java/info/frostfs/sdk/constants/FieldConst.java index fe85aad..636df86 100644 --- a/models/src/main/java/info/frostfs/sdk/constants/FieldConst.java +++ b/models/src/main/java/info/frostfs/sdk/constants/FieldConst.java @@ -8,4 +8,8 @@ public class FieldConst { public static final String ORIGIN_FIELD_NAME = "origin"; public static final String ORIGIN_SIGNATURE_FIELD_NAME = "origin_signature"; public static final String VERIFY_HEADER_FIELD_NAME = "verify_header"; + public static final String EMPTY_STRING = ""; + + private FieldConst() { + } } diff --git a/models/src/main/java/info/frostfs/sdk/constants/XHeaderConst.java b/models/src/main/java/info/frostfs/sdk/constants/XHeaderConst.java index c1cc83c..e8b2c72 100644 --- a/models/src/main/java/info/frostfs/sdk/constants/XHeaderConst.java +++ b/models/src/main/java/info/frostfs/sdk/constants/XHeaderConst.java @@ -4,4 +4,7 @@ public class XHeaderConst { public static final String RESERVED_XHEADER_PREFIX = "__SYSTEM__"; public static final String XHEADER_NETMAP_EPOCH = RESERVED_XHEADER_PREFIX + "NETMAP_EPOCH"; public static final String XHEADER_NETMAP_LOOKUP_DEPTH = RESERVED_XHEADER_PREFIX + "NETMAP_LOOKUP_DEPTH"; + + private XHeaderConst() { + } } diff --git a/models/src/main/java/info/frostfs/sdk/dto/MetaHeader.java b/models/src/main/java/info/frostfs/sdk/dto/MetaHeader.java index 4e22f46..9d6735e 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/MetaHeader.java +++ b/models/src/main/java/info/frostfs/sdk/dto/MetaHeader.java @@ -1,18 +1,28 @@ package info.frostfs.sdk.dto; +import static info.frostfs.sdk.constants.AppConst.DEFAULT_MAJOR_VERSION; +import static info.frostfs.sdk.constants.AppConst.DEFAULT_MINOR_VERSION; +import static java.util.Objects.isNull; + public class MetaHeader { private Version version; private int epoch; private int ttl; public MetaHeader(Version version, int epoch, int ttl) { + if (isNull(version) || epoch < 0 || ttl <= 0) { + throw new IllegalArgumentException("One of the input attributes is invalid or missing"); + } + this.version = version; this.epoch = epoch; this.ttl = ttl; } - public static MetaHeader getDefault() { - return new MetaHeader(new Version(2, 13), 0, 2); + public MetaHeader() { + this.version = new Version(DEFAULT_MAJOR_VERSION, DEFAULT_MINOR_VERSION); + this.epoch = 0; + this.ttl = 2; } public Version getVersion() { @@ -20,6 +30,10 @@ public class MetaHeader { } public void setVersion(Version version) { + if (isNull(version)) { + throw new IllegalArgumentException("Version is missing."); + } + this.version = version; } @@ -28,6 +42,10 @@ public class MetaHeader { } public void setEpoch(int epoch) { + if (epoch < 0) { + throw new IllegalArgumentException("The epoch must be greater than or equal to zero."); + } + this.epoch = epoch; } @@ -36,6 +54,10 @@ public class MetaHeader { } public void setTtl(int ttl) { + if (ttl <= 0) { + throw new IllegalArgumentException("The ttl must be greater than zero."); + } + this.ttl = ttl; } } diff --git a/models/src/main/java/info/frostfs/sdk/dto/OwnerId.java b/models/src/main/java/info/frostfs/sdk/dto/OwnerId.java index d4a173b..1603e21 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/OwnerId.java +++ b/models/src/main/java/info/frostfs/sdk/dto/OwnerId.java @@ -3,6 +3,7 @@ package info.frostfs.sdk.dto; import info.frostfs.sdk.Base58; import static info.frostfs.sdk.KeyExtension.publicKeyToAddress; +import static java.util.Objects.isNull; public class OwnerId { private final String value; @@ -11,8 +12,12 @@ public class OwnerId { this.value = value; } - public static OwnerId fromKey(byte[] publicKey) { - return new OwnerId(publicKeyToAddress(publicKey)); + public OwnerId(byte[] publicKey) { + if (isNull(publicKey) || publicKey.length == 0) { + throw new IllegalArgumentException("PublicKey is invalid"); + } + + this.value = publicKeyToAddress(publicKey); } public String getValue() { diff --git a/models/src/main/java/info/frostfs/sdk/dto/Split.java b/models/src/main/java/info/frostfs/sdk/dto/Split.java index 2b5726a..642ed58 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/Split.java +++ b/models/src/main/java/info/frostfs/sdk/dto/Split.java @@ -6,9 +6,11 @@ import info.frostfs.sdk.dto.object.ObjectId; import java.util.ArrayList; import java.util.List; +import static java.util.Objects.isNull; + public class Split { private final List children; - private SplitId splitId; + private final SplitId splitId; private ObjectId parent; private ObjectId previous; private Signature parentSignature; @@ -19,6 +21,10 @@ public class Split { } public Split(SplitId splitId) { + if (isNull(splitId)) { + throw new IllegalArgumentException("SplitId is not present"); + } + this.splitId = splitId; this.children = new ArrayList<>(); } @@ -27,10 +33,6 @@ public class Split { return splitId; } - private void setSplitId(SplitId splitId) { - this.splitId = splitId; - } - public ObjectId getParent() { return parent; } diff --git a/models/src/main/java/info/frostfs/sdk/dto/SplitId.java b/models/src/main/java/info/frostfs/sdk/dto/SplitId.java index 6ecc031..3ced690 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/SplitId.java +++ b/models/src/main/java/info/frostfs/sdk/dto/SplitId.java @@ -9,30 +9,22 @@ import static java.util.Objects.isNull; public class SplitId { private final UUID id; - public SplitId(UUID uuid) { - this.id = uuid; - } - public SplitId() { this.id = UUID.randomUUID(); } - private SplitId(byte[] binary) { + public SplitId(UUID uuid) { + this.id = uuid; + } + + public SplitId(byte[] binary) { this.id = asUuid(binary); } - private SplitId(String str) { + public SplitId(String str) { this.id = UUID.fromString(str); } - public static SplitId createFromBinary(byte[] binaryData) { - return new SplitId(binaryData); - } - - public static SplitId createFromString(String stringData) { - return new SplitId(stringData); - } - @Override public String toString() { return id.toString(); diff --git a/models/src/main/java/info/frostfs/sdk/dto/Status.java b/models/src/main/java/info/frostfs/sdk/dto/Status.java index dd242a3..e1075b5 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/Status.java +++ b/models/src/main/java/info/frostfs/sdk/dto/Status.java @@ -2,6 +2,7 @@ package info.frostfs.sdk.dto; import info.frostfs.sdk.enums.StatusCode; +import static info.frostfs.sdk.constants.FieldConst.EMPTY_STRING; import static java.util.Objects.isNull; public class Status { @@ -10,12 +11,12 @@ public class Status { public Status(StatusCode code, String message) { this.code = code; - this.message = isNull(message) ? "" : message; + this.message = isNull(message) ? EMPTY_STRING : message; } public Status(StatusCode code) { this.code = code; - this.message = ""; + this.message = EMPTY_STRING; } public StatusCode getCode() { diff --git a/models/src/main/java/info/frostfs/sdk/dto/Version.java b/models/src/main/java/info/frostfs/sdk/dto/Version.java index 563d8b6..0222922 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/Version.java +++ b/models/src/main/java/info/frostfs/sdk/dto/Version.java @@ -1,36 +1,41 @@ package info.frostfs.sdk.dto; +import static info.frostfs.sdk.constants.AppConst.DEFAULT_MAJOR_VERSION; +import static info.frostfs.sdk.constants.AppConst.DEFAULT_MINOR_VERSION; +import static java.util.Objects.isNull; + public class Version { - private int major; - private int minor; + private final int major; + private final int minor; public Version(int major, int minor) { this.major = major; this.minor = minor; } + public Version() { + this.major = DEFAULT_MAJOR_VERSION; + this.minor = DEFAULT_MINOR_VERSION; + } + public int getMajor() { return major; } - public void setMajor(int major) { - this.major = major; - } - public int getMinor() { return minor; } - public void setMinor(int minor) { - this.minor = minor; - } - @Override public String toString() { return "v" + major + "." + minor; } public boolean isSupported(Version version) { + if (isNull(version)) { + return false; + } + return major == version.getMajor(); } } diff --git a/models/src/main/java/info/frostfs/sdk/dto/container/ContainerId.java b/models/src/main/java/info/frostfs/sdk/dto/container/ContainerId.java index b24a069..1cd39b2 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/container/ContainerId.java +++ b/models/src/main/java/info/frostfs/sdk/dto/container/ContainerId.java @@ -2,29 +2,33 @@ package info.frostfs.sdk.dto.container; import info.frostfs.sdk.Base58; import info.frostfs.sdk.constants.AppConst; +import org.apache.commons.lang3.StringUtils; + +import static java.util.Objects.isNull; public class ContainerId { - private String value; + private final String value; public ContainerId(String value) { + if (StringUtils.isEmpty(value)) { + throw new IllegalArgumentException("ContainerId value is missing"); + } + this.value = value; } - public static ContainerId fromHash(byte[] hash) { - if (hash.length != AppConst.SHA256_HASH_LENGTH) { - throw new IllegalArgumentException("ContainerID must be a sha256 hash."); + public ContainerId(byte[] hash) { + if (isNull(hash) || hash.length != AppConst.SHA256_HASH_LENGTH) { + throw new IllegalArgumentException("ContainerId must be a sha256 hash."); } - return new ContainerId(Base58.encode(hash)); + + this.value = Base58.encode(hash); } public String getValue() { return value; } - public void setValue(String value) { - this.value = value; - } - @Override public String toString() { return value; diff --git a/models/src/main/java/info/frostfs/sdk/dto/netmap/Replica.java b/models/src/main/java/info/frostfs/sdk/dto/netmap/Replica.java index 235d288..74e3886 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/netmap/Replica.java +++ b/models/src/main/java/info/frostfs/sdk/dto/netmap/Replica.java @@ -1,34 +1,36 @@ package info.frostfs.sdk.dto.netmap; -import static java.util.Objects.isNull; +import org.apache.commons.lang3.StringUtils; + +import static info.frostfs.sdk.constants.FieldConst.EMPTY_STRING; public class Replica { - private int count; - private String selector; + private final int count; + private final String selector; public Replica(int count, String selector) { + if (count <= 0) { + throw new IllegalArgumentException("Replica count must be positive"); + } + this.count = count; - this.selector = isNull(selector) ? "" : selector; + this.selector = StringUtils.isEmpty(selector) ? EMPTY_STRING : selector; } public Replica(int count) { + if (count <= 0) { + throw new IllegalArgumentException("Replica count must be positive"); + } + this.count = count; - this.selector = ""; + this.selector = EMPTY_STRING; } public int getCount() { return count; } - public void setCount(int count) { - this.count = count; - } - public String getSelector() { return selector; } - - public void setSelector(String selector) { - this.selector = selector; - } } diff --git a/models/src/main/java/info/frostfs/sdk/dto/object/LargeObject.java b/models/src/main/java/info/frostfs/sdk/dto/object/LargeObject.java index 4cab2ed..915b745 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/object/LargeObject.java +++ b/models/src/main/java/info/frostfs/sdk/dto/object/LargeObject.java @@ -5,6 +5,7 @@ import info.frostfs.sdk.dto.container.ContainerId; import java.security.MessageDigest; import static info.frostfs.sdk.Helper.getSha256Instance; +import static java.util.Objects.isNull; public class LargeObject extends ObjectFrostFS { private final MessageDigest payloadHash; @@ -15,6 +16,10 @@ public class LargeObject extends ObjectFrostFS { } public void appendBlock(byte[] bytes, int count) { + if (count == 0 || isNull(bytes) || bytes.length == 0) { + return; + } + this.getHeader().increasePayloadLength(count); this.payloadHash.update(bytes, 0, count); } diff --git a/models/src/main/java/info/frostfs/sdk/dto/object/LinkObject.java b/models/src/main/java/info/frostfs/sdk/dto/object/LinkObject.java index b641888..d7a7091 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/object/LinkObject.java +++ b/models/src/main/java/info/frostfs/sdk/dto/object/LinkObject.java @@ -3,6 +3,7 @@ package info.frostfs.sdk.dto.object; import info.frostfs.sdk.dto.Split; import info.frostfs.sdk.dto.SplitId; import info.frostfs.sdk.dto.container.ContainerId; +import org.apache.commons.collections4.CollectionUtils; import java.util.List; @@ -16,6 +17,10 @@ public class LinkObject extends ObjectFrostFS { } public void addChildren(List objectIds) { + if (CollectionUtils.isEmpty(objectIds)) { + return; + } + this.getHeader().getSplit().getChildren().addAll(objectIds); } } diff --git a/models/src/main/java/info/frostfs/sdk/dto/object/ObjectAttribute.java b/models/src/main/java/info/frostfs/sdk/dto/object/ObjectAttribute.java index 0c2554b..60a3ede 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/object/ObjectAttribute.java +++ b/models/src/main/java/info/frostfs/sdk/dto/object/ObjectAttribute.java @@ -1,10 +1,16 @@ package info.frostfs.sdk.dto.object; +import org.apache.commons.lang3.StringUtils; + public class ObjectAttribute { - private String key; - private String value; + private final String key; + private final String value; public ObjectAttribute(String key, String value) { + if (StringUtils.isEmpty(key) || StringUtils.isEmpty(value)) { + throw new IllegalArgumentException("One of the input attributes is missing"); + } + this.key = key; this.value = value; } @@ -13,15 +19,7 @@ public class ObjectAttribute { return key; } - public void setKey(String key) { - this.key = key; - } - public String getValue() { return value; } - - public void setValue(String value) { - this.value = value; - } } diff --git a/models/src/main/java/info/frostfs/sdk/dto/object/ObjectFrostFS.java b/models/src/main/java/info/frostfs/sdk/dto/object/ObjectFrostFS.java index 5001161..af5f463 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/object/ObjectFrostFS.java +++ b/models/src/main/java/info/frostfs/sdk/dto/object/ObjectFrostFS.java @@ -10,34 +10,34 @@ import java.util.List; import static java.util.Objects.isNull; public class ObjectFrostFS { - private ObjectHeader header; + private final ObjectHeader header; private ObjectId objectId; private byte[] payload; public ObjectFrostFS(ObjectHeader header, ObjectId objectId, byte[] payload) { + if (isNull(header)) { + throw new IllegalArgumentException("Object header is missing"); + } + this.header = header; this.objectId = objectId; this.payload = payload; } - public ObjectFrostFS(ContainerId container, byte[] payload) { + public ObjectFrostFS(ContainerId containerId, byte[] payload) { this.payload = payload; - this.header = new ObjectHeader(container, new ArrayList<>()); + this.header = new ObjectHeader(containerId, new ArrayList<>()); } - public ObjectFrostFS(ContainerId container, byte[] payload, ObjectType objectType) { + public ObjectFrostFS(ContainerId containerId, byte[] payload, ObjectType objectType) { this.payload = payload; - this.header = new ObjectHeader(container, objectType, new ArrayList<>()); + this.header = new ObjectHeader(containerId, objectType, new ArrayList<>()); } public ObjectHeader getHeader() { return header; } - public void setHeader(ObjectHeader header) { - this.header = header; - } - public ObjectId getObjectId() { return objectId; } diff --git a/models/src/main/java/info/frostfs/sdk/dto/object/ObjectHeader.java b/models/src/main/java/info/frostfs/sdk/dto/object/ObjectHeader.java index 147ec4e..208b936 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/object/ObjectHeader.java +++ b/models/src/main/java/info/frostfs/sdk/dto/object/ObjectHeader.java @@ -8,11 +8,13 @@ import info.frostfs.sdk.enums.ObjectType; import java.util.List; +import static java.util.Objects.isNull; + public class ObjectHeader { + private final ContainerId containerId; + private final ObjectType objectType; private List attributes; - private ContainerId containerId; private long size; - private ObjectType objectType; private Version version; private OwnerId ownerId; private long payloadLength; @@ -21,6 +23,10 @@ public class ObjectHeader { public ObjectHeader(ContainerId containerId, ObjectType objectType, List attributes, long size, Version version) { + if (isNull(containerId) || isNull(objectType)) { + throw new IllegalArgumentException("ContainerId or objectType is not present"); + } + this.attributes = attributes; this.containerId = containerId; this.size = size; @@ -28,13 +34,21 @@ public class ObjectHeader { this.version = version; } - public ObjectHeader(ContainerId containerId, ObjectType type, List attributes) { + public ObjectHeader(ContainerId containerId, ObjectType objectType, List attributes) { + if (isNull(containerId) || isNull(objectType)) { + throw new IllegalArgumentException("ContainerId or objectType is not present"); + } + this.attributes = attributes; this.containerId = containerId; - this.objectType = type; + this.objectType = objectType; } public ObjectHeader(ContainerId containerId, List attributes) { + if (isNull(containerId)) { + throw new IllegalArgumentException("ContainerId is not present"); + } + this.attributes = attributes; this.containerId = containerId; this.objectType = ObjectType.REGULAR; @@ -73,6 +87,10 @@ public class ObjectHeader { } public void setSplit(Split split) { + if (isNull(split)) { + throw new IllegalArgumentException("Split is not present"); + } + this.split = split; } @@ -88,10 +106,6 @@ public class ObjectHeader { return containerId; } - public void setContainerId(ContainerId containerId) { - this.containerId = containerId; - } - public long getSize() { return size; } @@ -104,10 +118,6 @@ public class ObjectHeader { return objectType; } - public void setObjectType(ObjectType objectType) { - this.objectType = objectType; - } - public Version getVersion() { return version; } diff --git a/models/src/main/java/info/frostfs/sdk/dto/object/ObjectId.java b/models/src/main/java/info/frostfs/sdk/dto/object/ObjectId.java index 3c9965a..7c6bffb 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/object/ObjectId.java +++ b/models/src/main/java/info/frostfs/sdk/dto/object/ObjectId.java @@ -2,19 +2,27 @@ package info.frostfs.sdk.dto.object; import info.frostfs.sdk.Base58; import info.frostfs.sdk.constants.AppConst; +import org.apache.commons.lang3.StringUtils; + +import static java.util.Objects.isNull; public class ObjectId { private final String value; - public ObjectId(String id) { - this.value = id; + public ObjectId(String value) { + if (StringUtils.isEmpty(value)) { + throw new IllegalArgumentException("ObjectId value is missing"); + } + + this.value = value; } - public static ObjectId fromHash(byte[] hash) { - if (hash.length != AppConst.SHA256_HASH_LENGTH) { + public ObjectId(byte[] hash) { + if (isNull(hash) || hash.length != AppConst.SHA256_HASH_LENGTH) { throw new IllegalArgumentException("ObjectId must be a sha256 hash."); } - return new ObjectId(Base58.encode(hash)); + + this.value = Base58.encode(hash); } public String getValue() { diff --git a/models/src/main/java/info/frostfs/sdk/mappers/MetaHeaderMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/MetaHeaderMapper.java index 736871c..0b90453 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/MetaHeaderMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/MetaHeaderMapper.java @@ -3,9 +3,18 @@ package info.frostfs.sdk.mappers; import frostfs.session.Types; import info.frostfs.sdk.dto.MetaHeader; +import static java.util.Objects.isNull; + public class MetaHeaderMapper { + private MetaHeaderMapper() { + } + public static Types.RequestMetaHeader toGrpcMessage(MetaHeader metaHeader) { + if (isNull(metaHeader)) { + return null; + } + return Types.RequestMetaHeader.newBuilder() .setVersion(VersionMapper.toGrpcMessage(metaHeader.getVersion())) .setEpoch(metaHeader.getEpoch()) diff --git a/models/src/main/java/info/frostfs/sdk/mappers/OwnerIdMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/OwnerIdMapper.java index 69cb6e8..a0a6b64 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/OwnerIdMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/OwnerIdMapper.java @@ -4,9 +4,18 @@ import com.google.protobuf.ByteString; import frostfs.refs.Types; import info.frostfs.sdk.dto.OwnerId; +import static java.util.Objects.isNull; + public class OwnerIdMapper { + private OwnerIdMapper() { + } + public static Types.OwnerID toGrpcMessage(OwnerId ownerId) { + if (isNull(ownerId)) { + return null; + } + return Types.OwnerID.newBuilder() .setValue(ByteString.copyFrom(ownerId.toHash())) .build(); diff --git a/models/src/main/java/info/frostfs/sdk/mappers/SessionMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/SessionMapper.java index c13a61e..45331cf 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/SessionMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/SessionMapper.java @@ -6,9 +6,18 @@ import frostfs.session.Types; import java.io.IOException; +import static java.util.Objects.isNull; + public class SessionMapper { + private SessionMapper() { + } + public static byte[] serialize(Types.SessionToken token) { + if (isNull(token)) { + throw new IllegalArgumentException("Token is not present"); + } + try { byte[] bytes = new byte[token.getSerializedSize()]; CodedOutputStream stream = CodedOutputStream.newInstance(bytes); @@ -20,6 +29,10 @@ public class SessionMapper { } public static Types.SessionToken deserializeSessionToken(byte[] bytes) { + if (isNull(bytes) || bytes.length == 0) { + throw new IllegalArgumentException("Token is not present"); + } + try { return Types.SessionToken.newBuilder().mergeFrom(bytes).build(); } catch (InvalidProtocolBufferException exp) { diff --git a/models/src/main/java/info/frostfs/sdk/mappers/SignatureMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/SignatureMapper.java index 1a62bfd..5f14f4a 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/SignatureMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/SignatureMapper.java @@ -8,7 +8,14 @@ import static java.util.Objects.isNull; public class SignatureMapper { - public static Types.Signature ToGrpcMessage(Signature signature) { + private SignatureMapper() { + } + + public static Types.Signature toGrpcMessage(Signature signature) { + if (isNull(signature)) { + return null; + } + var scheme = Types.SignatureScheme.forNumber(signature.getScheme().value); if (isNull(scheme)) { throw new IllegalArgumentException( diff --git a/models/src/main/java/info/frostfs/sdk/mappers/StatusMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/StatusMapper.java index 7c6f3b1..2bfa717 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/StatusMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/StatusMapper.java @@ -8,8 +8,13 @@ import static java.util.Objects.isNull; public class StatusMapper { + private StatusMapper() { + } + public static Status toModel(Types.Status status) { - if (isNull(status)) return new Status(StatusCode.SUCCESS); + if (isNull(status)) { + return new Status(StatusCode.SUCCESS); + } var statusCode = StatusCode.get(status.getCode()); if (isNull(statusCode)) { diff --git a/models/src/main/java/info/frostfs/sdk/mappers/VersionMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/VersionMapper.java index 7888a67..13e7a0d 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/VersionMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/VersionMapper.java @@ -3,9 +3,18 @@ package info.frostfs.sdk.mappers; import frostfs.refs.Types; import info.frostfs.sdk.dto.Version; +import static java.util.Objects.isNull; + public class VersionMapper { + private VersionMapper() { + } + public static Types.Version toGrpcMessage(Version version) { + if (isNull(version)) { + return null; + } + return Types.Version.newBuilder() .setMajor(version.getMajor()) .setMinor(version.getMinor()) @@ -13,6 +22,10 @@ public class VersionMapper { } public static Version toModel(Types.Version version) { + if (isNull(version)) { + return null; + } + return new Version(version.getMajor(), version.getMinor()); } } diff --git a/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerIdMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerIdMapper.java index cbe7237..26e941c 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerIdMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerIdMapper.java @@ -4,9 +4,18 @@ import com.google.protobuf.ByteString; import frostfs.refs.Types; import info.frostfs.sdk.dto.container.ContainerId; +import static java.util.Objects.isNull; + public class ContainerIdMapper { + private ContainerIdMapper() { + } + public static Types.ContainerID toGrpcMessage(ContainerId containerId) { + if (isNull(containerId)) { + return null; + } + return Types.ContainerID.newBuilder() .setValue(ByteString.copyFrom(containerId.toHash())) .build(); diff --git a/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerMapper.java index ff36486..9aa1741 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerMapper.java @@ -13,7 +13,14 @@ import static java.util.Objects.isNull; public class ContainerMapper { + private ContainerMapper() { + } + public static Types.Container toGrpcMessage(Container container) { + if (isNull(container)) { + return null; + } + return Types.Container.newBuilder() .setBasicAcl(container.getBasicAcl().value) .setPlacementPolicy(PlacementPolicyMapper.toGrpcMessage(container.getPlacementPolicy())) @@ -22,6 +29,10 @@ public class ContainerMapper { } public static Container toModel(Types.Container containerGrpc) { + if (isNull(containerGrpc)) { + return null; + } + var basicAcl = BasicAcl.get(containerGrpc.getBasicAcl()); if (isNull(basicAcl)) { throw new IllegalArgumentException( diff --git a/models/src/main/java/info/frostfs/sdk/mappers/netmap/NetmapSnapshotMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/netmap/NetmapSnapshotMapper.java index a396f58..f911e74 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/netmap/NetmapSnapshotMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/netmap/NetmapSnapshotMapper.java @@ -5,9 +5,18 @@ import info.frostfs.sdk.dto.netmap.NetmapSnapshot; import java.util.stream.Collectors; +import static java.util.Objects.isNull; + public class NetmapSnapshotMapper { + private NetmapSnapshotMapper() { + } + public static NetmapSnapshot toModel(Service.NetmapSnapshotResponse netmap) { + if (isNull(netmap)) { + return null; + } + return new NetmapSnapshot( netmap.getBody().getNetmap().getEpoch(), netmap.getBody().getNetmap().getNodesList().stream() diff --git a/models/src/main/java/info/frostfs/sdk/mappers/netmap/NodeInfoMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/netmap/NodeInfoMapper.java index c2bd9b7..d91dcd1 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/netmap/NodeInfoMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/netmap/NodeInfoMapper.java @@ -13,11 +13,22 @@ import static java.util.Objects.isNull; public class NodeInfoMapper { + private NodeInfoMapper() { + } + public static NodeInfo toModel(Service.LocalNodeInfoResponse.Body nodeInfo) { + if (isNull(nodeInfo)) { + return null; + } + return toModel(nodeInfo.getNodeInfo(), nodeInfo.getVersion()); } public static NodeInfo toModel(frostfs.netmap.Types.NodeInfo nodeInfo, Types.Version version) { + if (isNull(nodeInfo)) { + return null; + } + NodeState nodeState = NodeState.get(nodeInfo.getState().getNumber()); if (isNull(nodeState)) { throw new IllegalArgumentException( diff --git a/models/src/main/java/info/frostfs/sdk/mappers/netmap/PlacementPolicyMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/netmap/PlacementPolicyMapper.java index 4a6675c..f5bdaef 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/netmap/PlacementPolicyMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/netmap/PlacementPolicyMapper.java @@ -4,9 +4,18 @@ import frostfs.netmap.Types; import info.frostfs.sdk.dto.netmap.PlacementPolicy; import info.frostfs.sdk.dto.netmap.Replica; +import static java.util.Objects.isNull; + public class PlacementPolicyMapper { + private PlacementPolicyMapper() { + } + public static Types.PlacementPolicy toGrpcMessage(PlacementPolicy placementPolicy) { + if (isNull(placementPolicy)) { + return null; + } + var pp = Types.PlacementPolicy.newBuilder() .setUnique(placementPolicy.isUnique()); @@ -18,6 +27,10 @@ public class PlacementPolicyMapper { } public static PlacementPolicy toModel(Types.PlacementPolicy placementPolicy) { + if (isNull(placementPolicy)) { + return null; + } + return new PlacementPolicy( placementPolicy.getUnique(), placementPolicy.getReplicasList().stream().map(ReplicaMapper::toModel).toArray(Replica[]::new) diff --git a/models/src/main/java/info/frostfs/sdk/mappers/netmap/ReplicaMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/netmap/ReplicaMapper.java index eef3e70..e25ec21 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/netmap/ReplicaMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/netmap/ReplicaMapper.java @@ -3,9 +3,18 @@ package info.frostfs.sdk.mappers.netmap; import frostfs.netmap.Types; import info.frostfs.sdk.dto.netmap.Replica; +import static java.util.Objects.isNull; + public class ReplicaMapper { + private ReplicaMapper() { + } + public static Types.Replica toGrpcMessage(Replica replica) { + if (isNull(replica)) { + return null; + } + return Types.Replica.newBuilder() .setCount(replica.getCount()) .setSelector(replica.getSelector()) @@ -13,6 +22,10 @@ public class ReplicaMapper { } public static Replica toModel(Types.Replica replica) { + if (isNull(replica)) { + return null; + } + return new Replica(replica.getCount(), replica.getSelector()); } } diff --git a/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectAttributeMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectAttributeMapper.java index 62c67cb..17c3e94 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectAttributeMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectAttributeMapper.java @@ -3,9 +3,18 @@ package info.frostfs.sdk.mappers.object; import frostfs.object.Types; import info.frostfs.sdk.dto.object.ObjectAttribute; +import static java.util.Objects.isNull; + public class ObjectAttributeMapper { + private ObjectAttributeMapper() { + } + public static Types.Header.Attribute toGrpcMessage(ObjectAttribute attribute) { + if (isNull(attribute)) { + return null; + } + return Types.Header.Attribute.newBuilder() .setKey(attribute.getKey()) .setValue(attribute.getValue()) @@ -13,6 +22,10 @@ public class ObjectAttributeMapper { } public static ObjectAttribute toModel(Types.Header.Attribute attribute) { + if (isNull(attribute)) { + return null; + } + return new ObjectAttribute(attribute.getKey(), attribute.getValue()); } } diff --git a/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectFilterMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectFilterMapper.java index 8bbe23e..b1c121d 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectFilterMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectFilterMapper.java @@ -8,7 +8,14 @@ import static java.util.Objects.isNull; public class ObjectFilterMapper { + private ObjectFilterMapper() { + } + public static Service.SearchRequest.Body.Filter toGrpcMessage(ObjectFilter filter) { + if (isNull(filter)) { + return null; + } + var objectMatchType = Types.MatchType.forNumber(filter.getMatchType().value); if (isNull(objectMatchType)) { throw new IllegalArgumentException( diff --git a/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectFrostFSMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectFrostFSMapper.java index 28b8332..aa5f698 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectFrostFSMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectFrostFSMapper.java @@ -4,13 +4,22 @@ import frostfs.object.Types; import info.frostfs.sdk.dto.object.ObjectFrostFS; import info.frostfs.sdk.dto.object.ObjectId; +import static java.util.Objects.isNull; + public class ObjectFrostFSMapper { - public static ObjectFrostFS toModel(Types.Object obj) { + private ObjectFrostFSMapper() { + } + + public static ObjectFrostFS toModel(Types.Object object) { + if (isNull(object)) { + return null; + } + return new ObjectFrostFS( - ObjectHeaderMapper.toModel(obj.getHeader()), - ObjectId.fromHash(obj.getObjectId().getValue().toByteArray()), - obj.getPayload().toByteArray() + ObjectHeaderMapper.toModel(object.getHeader()), + new ObjectId(object.getObjectId().getValue().toByteArray()), + object.getPayload().toByteArray() ); } } diff --git a/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectHeaderMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectHeaderMapper.java index 6b8bc91..fc5b4cc 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectHeaderMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectHeaderMapper.java @@ -14,7 +14,14 @@ import static java.util.Objects.isNull; public class ObjectHeaderMapper { + private ObjectHeaderMapper() { + } + public static Types.Header toGrpcMessage(ObjectHeader header) { + if (isNull(header)) { + return null; + } + var objectType = Types.ObjectType.forNumber(header.getObjectType().value); if (isNull(objectType)) { throw new IllegalArgumentException( @@ -34,6 +41,10 @@ public class ObjectHeaderMapper { } public static ObjectHeader toModel(Types.Header header) { + if (isNull(header)) { + return null; + } + var objectType = ObjectType.get(header.getObjectTypeValue()); if (isNull(objectType)) { throw new IllegalArgumentException( @@ -42,7 +53,7 @@ public class ObjectHeaderMapper { } return new ObjectHeader( - ContainerId.fromHash(header.getContainerId().getValue().toByteArray()), + new ContainerId(header.getContainerId().getValue().toByteArray()), objectType, header.getAttributesList().stream().map(ObjectAttributeMapper::toModel).collect(Collectors.toList()), header.getPayloadLength(), diff --git a/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectIdMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectIdMapper.java index 62ca7c5..af51095 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectIdMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectIdMapper.java @@ -4,15 +4,24 @@ import com.google.protobuf.ByteString; import frostfs.refs.Types; import info.frostfs.sdk.dto.object.ObjectId; +import static java.util.Objects.isNull; + public class ObjectIdMapper { + private ObjectIdMapper() { + } + public static Types.ObjectID toGrpcMessage(ObjectId objectId) { + if (isNull(objectId)) { + return null; + } + return Types.ObjectID.newBuilder() .setValue(ByteString.copyFrom(objectId.toHash())) .build(); } public static ObjectId toModel(Types.ObjectID objectId) { - return ObjectId.fromHash(objectId.getValue().toByteArray()); + return new ObjectId(objectId.getValue().toByteArray()); } } From 75188933889846e7dd3196754bfe03e27576536a Mon Sep 17 00:00:00 2001 From: Bruk Ori Date: Thu, 25 Jul 2024 12:24:05 +0300 Subject: [PATCH 09/38] [#1] update README.md Signed-off-by: Ori Bruk --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index bd5444b..fe3c04f 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ neo-go wallet export -w -d ## Example usage -### Container +### Container operations ```java import info.frostfs.sdk.dto.container.Container; @@ -50,7 +50,7 @@ public class ContainerExample { } ``` -### Object +### Object operations ```java import info.frostfs.sdk.enums.ObjectType; @@ -58,6 +58,7 @@ import info.frostfs.sdk.dto.container.ContainerId; import info.frostfs.sdk.dto.object.ObjectAttribute; import info.frostfs.sdk.dto.object.ObjectFilter; import info.frostfs.sdk.dto.object.ObjectHeader; +import info.frostfs.sdk.dto.object.ObjectId; import info.frostfs.sdk.jdo.PutObjectParameters; import info.frostfs.sdk.FrostFSClient; @@ -71,7 +72,7 @@ public class ObjectExample { FrostFSClient frostFSClient = new FrostFSClient(clientSettings); // Put object - info.frostfs.sdk.dto.object.ObjectId objectId; + ObjectId objectId; try (FileInputStream fis = new FileInputStream("cat.jpg")) { var cat = new ObjectHeader( containerId, ObjectType.REGULAR, new ObjectAttribute[]{new ObjectAttribute("Filename", "cat.jpg")} From ab8a574d0d62d34ba820fb0437dd79ab0cd03781 Mon Sep 17 00:00:00 2001 From: Bruk Ori Date: Tue, 30 Jul 2024 14:43:31 +0300 Subject: [PATCH 10/38] [#4] add checkstyle Signed-off-by: Ori Bruk --- checkstyle.xml | 74 +++++++++++++++++++ .../frostfs/sdk/jdo/ClientEnvironment.java | 2 +- .../sdk/services/impl/NetmapClientImpl.java | 2 +- .../sdk/services/impl/ObjectToolsImpl.java | 6 +- .../info/frostfs/sdk/tools/RequestSigner.java | 3 +- .../java/info/frostfs/sdk/tools/Verifier.java | 3 +- .../main/java/info/frostfs/sdk/Base58.java | 14 ++-- .../main/java/info/frostfs/sdk/Helper.java | 6 +- .../java/info/frostfs/sdk/KeyExtension.java | 11 +-- ...{UUIDExtension.java => UuidExtension.java} | 6 +- .../info/frostfs/sdk/constants/AppConst.java | 6 +- .../java/info/frostfs/sdk/dto/SplitId.java | 4 +- .../mappers/container/ContainerMapper.java | 4 +- pom.xml | 23 ++++++ 14 files changed, 139 insertions(+), 25 deletions(-) create mode 100644 checkstyle.xml rename models/src/main/java/info/frostfs/sdk/{UUIDExtension.java => UuidExtension.java} (87%) diff --git a/checkstyle.xml b/checkstyle.xml new file mode 100644 index 0000000..78ea3ce --- /dev/null +++ b/checkstyle.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/src/main/java/info/frostfs/sdk/jdo/ClientEnvironment.java b/client/src/main/java/info/frostfs/sdk/jdo/ClientEnvironment.java index 6b140d3..6e17947 100644 --- a/client/src/main/java/info/frostfs/sdk/jdo/ClientEnvironment.java +++ b/client/src/main/java/info/frostfs/sdk/jdo/ClientEnvironment.java @@ -1,8 +1,8 @@ package info.frostfs.sdk.jdo; +import info.frostfs.sdk.FrostFSClient; import info.frostfs.sdk.dto.OwnerId; import info.frostfs.sdk.dto.Version; -import info.frostfs.sdk.FrostFSClient; import io.grpc.Channel; import org.apache.commons.lang3.StringUtils; diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/NetmapClientImpl.java b/client/src/main/java/info/frostfs/sdk/services/impl/NetmapClientImpl.java index b38624c..3a3d1ca 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/NetmapClientImpl.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/NetmapClientImpl.java @@ -40,7 +40,7 @@ public class NetmapClientImpl extends ContextAccessor implements NetmapClient { private static long getLongValue(byte[] bytes) { long val = 0; for (var i = bytes.length - 1; i >= 0; i--) { - val = (val << 8) + bytes[i]; + val = (val << Byte.SIZE) + bytes[i]; } return val; diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/ObjectToolsImpl.java b/client/src/main/java/info/frostfs/sdk/services/impl/ObjectToolsImpl.java index b68a26e..2fb3b42 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/ObjectToolsImpl.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/ObjectToolsImpl.java @@ -49,7 +49,11 @@ public class ObjectToolsImpl extends ContextAccessor implements ToolsClient { var split = objectFrostFs.getHeader().getSplit(); if (nonNull(split)) { var splitGrpc = Types.Header.Split.newBuilder() - .setSplitId(nonNull(split.getSplitId()) ? ByteString.copyFrom(split.getSplitId().toBinary()) : null); + .setSplitId( + nonNull(split.getSplitId()) + ? ByteString.copyFrom(split.getSplitId().toBinary()) + : null + ); ListUtils.emptyIfNull(split.getChildren()).stream() .map(ObjectIdMapper::toGrpcMessage) diff --git a/client/src/main/java/info/frostfs/sdk/tools/RequestSigner.java b/client/src/main/java/info/frostfs/sdk/tools/RequestSigner.java index ea7d828..5cd178f 100644 --- a/client/src/main/java/info/frostfs/sdk/tools/RequestSigner.java +++ b/client/src/main/java/info/frostfs/sdk/tools/RequestSigner.java @@ -23,12 +23,13 @@ import static org.bouncycastle.util.BigIntegers.asUnsignedByteArray; public class RequestSigner { public static final String ERROR_UNSUPPORTED_TYPE_TEMPLATE = "Unsupported message type: %s"; public static final int RFC6979_SIGNATURE_SIZE = 64; + public static final int HASH_SIGNATURE_SIZE = 65; private RequestSigner() { } public static byte[] signData(ECDsa key, byte[] data) { - var hash = new byte[65]; + var hash = new byte[HASH_SIGNATURE_SIZE]; hash[0] = 0x04; try { Signature signature = Signature.getInstance(CryptoConst.SIGNATURE_ALGORITHM); diff --git a/client/src/main/java/info/frostfs/sdk/tools/Verifier.java b/client/src/main/java/info/frostfs/sdk/tools/Verifier.java index 48a2afc..f0cd408 100644 --- a/client/src/main/java/info/frostfs/sdk/tools/Verifier.java +++ b/client/src/main/java/info/frostfs/sdk/tools/Verifier.java @@ -82,7 +82,8 @@ public class Verifier { public static boolean verify(Message response) { var body = MessageHelper.getField(response, BODY_FIELD_NAME); var metaHeader = (Types.ResponseMetaHeader) MessageHelper.getField(response, META_HEADER_FIELD_NAME); - var verifyHeader = (Types.ResponseVerificationHeader) MessageHelper.getField(response, VERIFY_HEADER_FIELD_NAME); + var verifyHeader = (Types.ResponseVerificationHeader) + MessageHelper.getField(response, VERIFY_HEADER_FIELD_NAME); return verifyMatryoshkaLevel(body, metaHeader, verifyHeader); } diff --git a/cryptography/src/main/java/info/frostfs/sdk/Base58.java b/cryptography/src/main/java/info/frostfs/sdk/Base58.java index 6321d58..860299d 100644 --- a/cryptography/src/main/java/info/frostfs/sdk/Base58.java +++ b/cryptography/src/main/java/info/frostfs/sdk/Base58.java @@ -10,8 +10,12 @@ import static java.util.Objects.isNull; public class Base58 { public static final char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray(); + public static final int BASE58_SYMBOL_COUNT = 58; + public static final int BASE256_SYMBOL_COUNT = 256; + private static final int BYTE_DIVISION = 0xFF; private static final char ENCODED_ZERO = ALPHABET[0]; - private static final int[] INDEXES = new int[128]; + private static final char BASE58_ASCII_MAX_VALUE = 128; + private static final int[] INDEXES = new int[BASE58_ASCII_MAX_VALUE]; static { Arrays.fill(INDEXES, -1); @@ -69,7 +73,7 @@ public class Base58 { char[] encoded = new char[input.length * 2]; // upper bound int outputStart = encoded.length; for (int inputStart = zeros; inputStart < input.length; ) { - encoded[--outputStart] = ALPHABET[divmod(input, inputStart, 256, 58)]; + encoded[--outputStart] = ALPHABET[divmod(input, inputStart, BASE256_SYMBOL_COUNT, BASE58_SYMBOL_COUNT)]; if (input[inputStart] == 0) { ++inputStart; // optimization - skip leading zeros } @@ -93,7 +97,7 @@ public class Base58 { byte[] input58 = new byte[input.length()]; for (int i = 0; i < input.length(); ++i) { char c = input.charAt(i); - int digit = c < 128 ? INDEXES[c] : -1; + int digit = c < BASE58_ASCII_MAX_VALUE ? INDEXES[c] : -1; if (digit < 0) { throw new IllegalArgumentException(String.format("Invalid character in Base58: 0x%04x", (int) c)); } @@ -108,7 +112,7 @@ public class Base58 { byte[] decoded = new byte[input.length()]; int outputStart = decoded.length; for (int inputStart = zeros; inputStart < input58.length; ) { - decoded[--outputStart] = divmod(input58, inputStart, 58, 256); + decoded[--outputStart] = divmod(input58, inputStart, BASE58_SYMBOL_COUNT, BASE256_SYMBOL_COUNT); if (input58[inputStart] == 0) { ++inputStart; // optimization - skip leading zeros } @@ -125,7 +129,7 @@ public class Base58 { // this is just long division which accounts for the base of the input digits int remainder = 0; for (int i = firstDigit; i < number.length; i++) { - int digit = (int) number[i] & 0xFF; + int digit = (int) number[i] & BYTE_DIVISION; int temp = remainder * base + digit; number[i] = (byte) (temp / divisor); remainder = temp % divisor; diff --git a/cryptography/src/main/java/info/frostfs/sdk/Helper.java b/cryptography/src/main/java/info/frostfs/sdk/Helper.java index 58b8c1e..a864301 100644 --- a/cryptography/src/main/java/info/frostfs/sdk/Helper.java +++ b/cryptography/src/main/java/info/frostfs/sdk/Helper.java @@ -11,15 +11,17 @@ import java.security.NoSuchAlgorithmException; import static java.util.Objects.isNull; public class Helper { + private static final int RIPEMD_160_HASH_BYTE_LENGTH = 20; + private Helper() { } - public static byte[] getRIPEMD160(byte[] value) { + public static byte[] getRipemd160(byte[] value) { if (isNull(value)) { throw new IllegalArgumentException("Input value is missing"); } - var hash = new byte[20]; + var hash = new byte[RIPEMD_160_HASH_BYTE_LENGTH]; var digest = new RIPEMD160Digest(); digest.update(value, 0, value.length); digest.doFinal(hash, 0); diff --git a/cryptography/src/main/java/info/frostfs/sdk/KeyExtension.java b/cryptography/src/main/java/info/frostfs/sdk/KeyExtension.java index 40affe5..f3cf7de 100644 --- a/cryptography/src/main/java/info/frostfs/sdk/KeyExtension.java +++ b/cryptography/src/main/java/info/frostfs/sdk/KeyExtension.java @@ -24,13 +24,14 @@ import java.security.spec.ECPublicKeySpec; import java.security.spec.InvalidKeySpecException; import java.util.Arrays; -import static info.frostfs.sdk.Helper.getRIPEMD160; +import static info.frostfs.sdk.Helper.getRipemd160; import static info.frostfs.sdk.Helper.getSha256; import static java.util.Objects.isNull; import static org.bouncycastle.util.BigIntegers.fromUnsignedByteArray; public class KeyExtension { public static final byte NEO_ADDRESS_VERSION = 0x35; + private static final int PS_IN_HASH160 = 0x0C; private static final int DECODE_ADDRESS_LENGTH = 21; private static final int COMPRESSED_PUBLIC_KEY_LENGTH = 33; private static final int UNCOMPRESSED_PUBLIC_KEY_LENGTH = 65; @@ -134,7 +135,7 @@ public class KeyExtension { checkInputValue(publicKey); var script = createSignatureRedeemScript(publicKey); - return getRIPEMD160(getSha256(script)); + return getRipemd160(getSha256(script)); } public static String publicKeyToAddress(byte[] publicKey) { @@ -161,7 +162,7 @@ public class KeyExtension { byte[] buffer = new byte[4]; for (int i = 0; i < buffer.length; i++) { - buffer[i] = (byte) (value >> i * 8); + buffer[i] = (byte) (value >> i * Byte.SIZE); } return buffer; @@ -176,10 +177,10 @@ public class KeyExtension { ); } - var script = new byte[]{0x0c, COMPRESSED_PUBLIC_KEY_LENGTH}; //PUSHDATA1 33 + var script = new byte[]{PS_IN_HASH160, COMPRESSED_PUBLIC_KEY_LENGTH}; //PUSHDATA1 33 script = ArrayHelper.concat(script, publicKey); - script = ArrayHelper.concat(script, new byte[]{0x41}); //SYSCALL + script = ArrayHelper.concat(script, new byte[]{UNCOMPRESSED_PUBLIC_KEY_LENGTH}); //SYSCALL script = ArrayHelper.concat(script, getBytes(CHECK_SIG_DESCRIPTOR)); //Neo_Crypto_CheckSig return script; } diff --git a/models/src/main/java/info/frostfs/sdk/UUIDExtension.java b/models/src/main/java/info/frostfs/sdk/UuidExtension.java similarity index 87% rename from models/src/main/java/info/frostfs/sdk/UUIDExtension.java rename to models/src/main/java/info/frostfs/sdk/UuidExtension.java index 730a1a5..dc4c6ff 100644 --- a/models/src/main/java/info/frostfs/sdk/UUIDExtension.java +++ b/models/src/main/java/info/frostfs/sdk/UuidExtension.java @@ -5,10 +5,10 @@ import java.util.UUID; import static java.util.Objects.isNull; -public class UUIDExtension { +public class UuidExtension { private static final int UUID_BYTE_ARRAY_LENGTH = 16; - private UUIDExtension() { + private UuidExtension() { } public static UUID asUuid(byte[] bytes) { @@ -27,7 +27,7 @@ public class UUIDExtension { throw new IllegalArgumentException("Uuid is not present"); } - ByteBuffer bb = ByteBuffer.allocate(16); + ByteBuffer bb = ByteBuffer.allocate(UUID_BYTE_ARRAY_LENGTH); bb.putLong(uuid.getMostSignificantBits()); bb.putLong(uuid.getLeastSignificantBits()); return bb.array(); diff --git a/models/src/main/java/info/frostfs/sdk/constants/AppConst.java b/models/src/main/java/info/frostfs/sdk/constants/AppConst.java index a823466..d535ee7 100644 --- a/models/src/main/java/info/frostfs/sdk/constants/AppConst.java +++ b/models/src/main/java/info/frostfs/sdk/constants/AppConst.java @@ -3,7 +3,11 @@ package info.frostfs.sdk.constants; public class AppConst { public static final int DEFAULT_MAJOR_VERSION = 2; public static final int DEFAULT_MINOR_VERSION = 13; - public static final int OBJECT_CHUNK_SIZE = 3 * (1 << 20); + public static final int BYTE_SHIFT = 10; + public static final int BYTE = 1; + public static final int KIB = BYTE << BYTE_SHIFT; + public static final int MIB = KIB << BYTE_SHIFT; + public static final int OBJECT_CHUNK_SIZE = 3 * MIB; public static final int SHA256_HASH_LENGTH = 32; private AppConst() { diff --git a/models/src/main/java/info/frostfs/sdk/dto/SplitId.java b/models/src/main/java/info/frostfs/sdk/dto/SplitId.java index 3ced690..29ed2d0 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/SplitId.java +++ b/models/src/main/java/info/frostfs/sdk/dto/SplitId.java @@ -2,8 +2,8 @@ package info.frostfs.sdk.dto; import java.util.UUID; -import static info.frostfs.sdk.UUIDExtension.asBytes; -import static info.frostfs.sdk.UUIDExtension.asUuid; +import static info.frostfs.sdk.UuidExtension.asBytes; +import static info.frostfs.sdk.UuidExtension.asUuid; import static java.util.Objects.isNull; public class SplitId { diff --git a/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerMapper.java index 9aa1741..becfb22 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerMapper.java @@ -7,8 +7,8 @@ import info.frostfs.sdk.enums.BasicAcl; import info.frostfs.sdk.mappers.VersionMapper; import info.frostfs.sdk.mappers.netmap.PlacementPolicyMapper; -import static info.frostfs.sdk.UUIDExtension.asBytes; -import static info.frostfs.sdk.UUIDExtension.asUuid; +import static info.frostfs.sdk.UuidExtension.asBytes; +import static info.frostfs.sdk.UuidExtension.asUuid; import static java.util.Objects.isNull; public class ContainerMapper { diff --git a/pom.xml b/pom.xml index 561413c..16dc005 100644 --- a/pom.xml +++ b/pom.xml @@ -19,5 +19,28 @@ 11 11 UTF-8 + checkstyle.xml + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + + checkstyle + validate + + check + + + true + + + + 3.4.0 + + + \ No newline at end of file From 5f1d89d4074fa772bc912e5aea224888e21f7fb4 Mon Sep 17 00:00:00 2001 From: Bruk Ori Date: Wed, 21 Aug 2024 10:56:01 +0300 Subject: [PATCH 11/38] [#6] add junit dependency Signed-off-by: Ori Bruk --- pom.xml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/pom.xml b/pom.xml index 16dc005..9b5a976 100644 --- a/pom.xml +++ b/pom.xml @@ -20,8 +20,32 @@ 11 UTF-8 checkstyle.xml + 5.12.0 + 5.10.3 + 3.26.3 + + + org.junit.jupiter + junit-jupiter + ${junit.version} + test + + + org.assertj + assertj-core + ${assertj.version} + test + + + org.mockito + mockito-core + ${mockito.version} + test + + + From 441d3129dc9a60abe8134bbb6ee5e240b3669daf Mon Sep 17 00:00:00 2001 From: Bruk Ori Date: Wed, 21 Aug 2024 11:12:33 +0300 Subject: [PATCH 12/38] [#6] cover the models module with junit tests Signed-off-by: Ori Bruk --- .../java/info/frostfs/sdk/UuidExtension.java | 6 +- .../frostfs/sdk/dto/object/ObjectHeader.java | 13 +- .../frostfs/sdk/mappers/SessionMapper.java | 5 +- .../frostfs/sdk/mappers/SignatureMapper.java | 3 +- .../frostfs/sdk/mappers/StatusMapper.java | 3 +- .../frostfs/sdk/mappers/VersionMapper.java | 2 +- .../mappers/container/ContainerMapper.java | 5 +- .../mappers/netmap/NetmapSnapshotMapper.java | 2 +- .../sdk/mappers/netmap/NodeInfoMapper.java | 13 +- .../mappers/netmap/PlacementPolicyMapper.java | 2 +- .../sdk/mappers/netmap/ReplicaMapper.java | 2 +- .../mappers/object/ObjectAttributeMapper.java | 2 +- .../mappers/object/ObjectFilterMapper.java | 3 +- .../mappers/object/ObjectFrostFSMapper.java | 2 +- .../mappers/object/ObjectHeaderMapper.java | 12 +- .../sdk/mappers/object/ObjectIdMapper.java | 4 + .../info/frostfs/sdk/UuidExtensionTest.java | 57 ++++++++ .../sdk/mappers/MetaHeaderMapperTest.java | 33 +++++ .../sdk/mappers/OwnerIdMapperTest.java | 35 +++++ .../sdk/mappers/SessionMapperTest.java | 67 +++++++++ .../sdk/mappers/SignatureMapperTest.java | 56 ++++++++ .../frostfs/sdk/mappers/StatusMapperTest.java | 53 ++++++++ .../sdk/mappers/VersionMapperTest.java | 54 ++++++++ .../container/ContainerIdMapperTest.java | 28 ++++ .../container/ContainerMapperTest.java | 116 ++++++++++++++++ .../netmap/NetmapSnapshotMapperTest.java | 98 ++++++++++++++ .../mappers/netmap/NodeInfoMapperTest.java | 123 +++++++++++++++++ .../netmap/PlacementPolicyMapperTest.java | 77 +++++++++++ .../sdk/mappers/netmap/ReplicaMapperTest.java | 54 ++++++++ .../object/ObjectAttributeMapperTest.java | 54 ++++++++ .../object/ObjectFilterMapperTest.java | 53 ++++++++ .../object/ObjectFrostFSMapperTest.java | 60 +++++++++ .../object/ObjectHeaderMapperTest.java | 127 ++++++++++++++++++ .../mappers/object/ObjectIdMapperTest.java | 53 ++++++++ 34 files changed, 1240 insertions(+), 37 deletions(-) create mode 100644 models/src/test/java/info/frostfs/sdk/UuidExtensionTest.java create mode 100644 models/src/test/java/info/frostfs/sdk/mappers/MetaHeaderMapperTest.java create mode 100644 models/src/test/java/info/frostfs/sdk/mappers/OwnerIdMapperTest.java create mode 100644 models/src/test/java/info/frostfs/sdk/mappers/SessionMapperTest.java create mode 100644 models/src/test/java/info/frostfs/sdk/mappers/SignatureMapperTest.java create mode 100644 models/src/test/java/info/frostfs/sdk/mappers/StatusMapperTest.java create mode 100644 models/src/test/java/info/frostfs/sdk/mappers/VersionMapperTest.java create mode 100644 models/src/test/java/info/frostfs/sdk/mappers/container/ContainerIdMapperTest.java create mode 100644 models/src/test/java/info/frostfs/sdk/mappers/container/ContainerMapperTest.java create mode 100644 models/src/test/java/info/frostfs/sdk/mappers/netmap/NetmapSnapshotMapperTest.java create mode 100644 models/src/test/java/info/frostfs/sdk/mappers/netmap/NodeInfoMapperTest.java create mode 100644 models/src/test/java/info/frostfs/sdk/mappers/netmap/PlacementPolicyMapperTest.java create mode 100644 models/src/test/java/info/frostfs/sdk/mappers/netmap/ReplicaMapperTest.java create mode 100644 models/src/test/java/info/frostfs/sdk/mappers/object/ObjectAttributeMapperTest.java create mode 100644 models/src/test/java/info/frostfs/sdk/mappers/object/ObjectFilterMapperTest.java create mode 100644 models/src/test/java/info/frostfs/sdk/mappers/object/ObjectFrostFSMapperTest.java create mode 100644 models/src/test/java/info/frostfs/sdk/mappers/object/ObjectHeaderMapperTest.java create mode 100644 models/src/test/java/info/frostfs/sdk/mappers/object/ObjectIdMapperTest.java diff --git a/models/src/main/java/info/frostfs/sdk/UuidExtension.java b/models/src/main/java/info/frostfs/sdk/UuidExtension.java index dc4c6ff..9cec1c2 100644 --- a/models/src/main/java/info/frostfs/sdk/UuidExtension.java +++ b/models/src/main/java/info/frostfs/sdk/UuidExtension.java @@ -7,13 +7,15 @@ import static java.util.Objects.isNull; public class UuidExtension { private static final int UUID_BYTE_ARRAY_LENGTH = 16; + private static final String ERROR_WRONG_UUID_SIZE = "Uuid byte array length must be " + UUID_BYTE_ARRAY_LENGTH; + private static final String ERROR_UUID_MISSING = "Uuid is not present"; private UuidExtension() { } public static UUID asUuid(byte[] bytes) { if (isNull(bytes) || bytes.length != UUID_BYTE_ARRAY_LENGTH) { - throw new IllegalArgumentException("Uuid byte array length must be " + UUID_BYTE_ARRAY_LENGTH); + throw new IllegalArgumentException(ERROR_WRONG_UUID_SIZE); } ByteBuffer bb = ByteBuffer.wrap(bytes); @@ -24,7 +26,7 @@ public class UuidExtension { public static byte[] asBytes(UUID uuid) { if (isNull(uuid)) { - throw new IllegalArgumentException("Uuid is not present"); + throw new IllegalArgumentException(ERROR_UUID_MISSING); } ByteBuffer bb = ByteBuffer.allocate(UUID_BYTE_ARRAY_LENGTH); diff --git a/models/src/main/java/info/frostfs/sdk/dto/object/ObjectHeader.java b/models/src/main/java/info/frostfs/sdk/dto/object/ObjectHeader.java index 208b936..9cc1f47 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/object/ObjectHeader.java +++ b/models/src/main/java/info/frostfs/sdk/dto/object/ObjectHeader.java @@ -14,7 +14,6 @@ public class ObjectHeader { private final ContainerId containerId; private final ObjectType objectType; private List attributes; - private long size; private Version version; private OwnerId ownerId; private long payloadLength; @@ -22,14 +21,14 @@ public class ObjectHeader { private Split split; public ObjectHeader(ContainerId containerId, ObjectType objectType, - List attributes, long size, Version version) { + List attributes, long payloadLength, Version version) { if (isNull(containerId) || isNull(objectType)) { throw new IllegalArgumentException("ContainerId or objectType is not present"); } this.attributes = attributes; this.containerId = containerId; - this.size = size; + this.payloadLength = payloadLength; this.objectType = objectType; this.version = version; } @@ -106,14 +105,6 @@ public class ObjectHeader { return containerId; } - public long getSize() { - return size; - } - - public void setSize(long size) { - this.size = size; - } - public ObjectType getObjectType() { return objectType; } diff --git a/models/src/main/java/info/frostfs/sdk/mappers/SessionMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/SessionMapper.java index 45331cf..ee3deb7 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/SessionMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/SessionMapper.java @@ -9,13 +9,14 @@ import java.io.IOException; import static java.util.Objects.isNull; public class SessionMapper { + private static final String ERROR_TOKEN_MISSING_ERROR = "Token is not present"; private SessionMapper() { } public static byte[] serialize(Types.SessionToken token) { if (isNull(token)) { - throw new IllegalArgumentException("Token is not present"); + throw new IllegalArgumentException(ERROR_TOKEN_MISSING_ERROR); } try { @@ -30,7 +31,7 @@ public class SessionMapper { public static Types.SessionToken deserializeSessionToken(byte[] bytes) { if (isNull(bytes) || bytes.length == 0) { - throw new IllegalArgumentException("Token is not present"); + throw new IllegalArgumentException(ERROR_TOKEN_MISSING_ERROR); } try { diff --git a/models/src/main/java/info/frostfs/sdk/mappers/SignatureMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/SignatureMapper.java index 5f14f4a..5e9e103 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/SignatureMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/SignatureMapper.java @@ -7,6 +7,7 @@ import info.frostfs.sdk.dto.Signature; import static java.util.Objects.isNull; public class SignatureMapper { + private static final String ERROR_UNKNOWN_VALUE_TEMPLATE = "Unknown SignatureScheme. Value: %s."; private SignatureMapper() { } @@ -19,7 +20,7 @@ public class SignatureMapper { var scheme = Types.SignatureScheme.forNumber(signature.getScheme().value); if (isNull(scheme)) { throw new IllegalArgumentException( - String.format("Unknown SignatureScheme. Value: %s.", signature.getScheme().name()) + String.format(ERROR_UNKNOWN_VALUE_TEMPLATE, signature.getScheme().name()) ); } diff --git a/models/src/main/java/info/frostfs/sdk/mappers/StatusMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/StatusMapper.java index 2bfa717..04ddd6e 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/StatusMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/StatusMapper.java @@ -7,6 +7,7 @@ import info.frostfs.sdk.enums.StatusCode; import static java.util.Objects.isNull; public class StatusMapper { + private static final String ERROR_UNKNOWN_VALUE_TEMPLATE = "Unknown StatusCode. Value: %s."; private StatusMapper() { } @@ -19,7 +20,7 @@ public class StatusMapper { var statusCode = StatusCode.get(status.getCode()); if (isNull(statusCode)) { throw new IllegalArgumentException( - String.format("Unknown StatusCode. Value: %s.", status.getCode()) + String.format(ERROR_UNKNOWN_VALUE_TEMPLATE, status.getCode()) ); } diff --git a/models/src/main/java/info/frostfs/sdk/mappers/VersionMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/VersionMapper.java index 13e7a0d..f12cb73 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/VersionMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/VersionMapper.java @@ -22,7 +22,7 @@ public class VersionMapper { } public static Version toModel(Types.Version version) { - if (isNull(version)) { + if (isNull(version) || version.getSerializedSize() == 0) { return null; } diff --git a/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerMapper.java index becfb22..c02bfcb 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerMapper.java @@ -12,6 +12,7 @@ import static info.frostfs.sdk.UuidExtension.asUuid; import static java.util.Objects.isNull; public class ContainerMapper { + private static final String ERROR_UNKNOWN_VALUE_TEMPLATE = "Unknown BasicACL rule. Value: %s."; private ContainerMapper() { } @@ -29,14 +30,14 @@ public class ContainerMapper { } public static Container toModel(Types.Container containerGrpc) { - if (isNull(containerGrpc)) { + if (isNull(containerGrpc) || containerGrpc.getSerializedSize() == 0) { return null; } var basicAcl = BasicAcl.get(containerGrpc.getBasicAcl()); if (isNull(basicAcl)) { throw new IllegalArgumentException( - String.format("Unknown BasicACL rule. Value: %s.", containerGrpc.getBasicAcl()) + String.format(ERROR_UNKNOWN_VALUE_TEMPLATE, containerGrpc.getBasicAcl()) ); } diff --git a/models/src/main/java/info/frostfs/sdk/mappers/netmap/NetmapSnapshotMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/netmap/NetmapSnapshotMapper.java index f911e74..b7abf4e 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/netmap/NetmapSnapshotMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/netmap/NetmapSnapshotMapper.java @@ -13,7 +13,7 @@ public class NetmapSnapshotMapper { } public static NetmapSnapshot toModel(Service.NetmapSnapshotResponse netmap) { - if (isNull(netmap)) { + if (isNull(netmap) || netmap.getSerializedSize() == 0) { return null; } diff --git a/models/src/main/java/info/frostfs/sdk/mappers/netmap/NodeInfoMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/netmap/NodeInfoMapper.java index d91dcd1..a5791b3 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/netmap/NodeInfoMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/netmap/NodeInfoMapper.java @@ -1,8 +1,8 @@ package info.frostfs.sdk.mappers.netmap; import frostfs.netmap.Service; +import frostfs.netmap.Types; import frostfs.netmap.Types.NodeInfo.Attribute; -import frostfs.refs.Types; import info.frostfs.sdk.dto.netmap.NodeInfo; import info.frostfs.sdk.enums.NodeState; import info.frostfs.sdk.mappers.VersionMapper; @@ -12,28 +12,27 @@ import java.util.stream.Collectors; import static java.util.Objects.isNull; public class NodeInfoMapper { + private static final String ERROR_UNKNOWN_VALUE_TEMPLATE = "Unknown NodeState. Value: %s."; private NodeInfoMapper() { } public static NodeInfo toModel(Service.LocalNodeInfoResponse.Body nodeInfo) { - if (isNull(nodeInfo)) { + if (isNull(nodeInfo) || nodeInfo.getSerializedSize() == 0) { return null; } return toModel(nodeInfo.getNodeInfo(), nodeInfo.getVersion()); } - public static NodeInfo toModel(frostfs.netmap.Types.NodeInfo nodeInfo, Types.Version version) { - if (isNull(nodeInfo)) { + public static NodeInfo toModel(Types.NodeInfo nodeInfo, frostfs.refs.Types.Version version) { + if (isNull(nodeInfo) || nodeInfo.getSerializedSize() == 0) { return null; } NodeState nodeState = NodeState.get(nodeInfo.getState().getNumber()); if (isNull(nodeState)) { - throw new IllegalArgumentException( - String.format("Unknown NodeState. Value: %s.", nodeInfo.getState()) - ); + throw new IllegalArgumentException(String.format(ERROR_UNKNOWN_VALUE_TEMPLATE, nodeInfo.getState())); } return new NodeInfo( diff --git a/models/src/main/java/info/frostfs/sdk/mappers/netmap/PlacementPolicyMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/netmap/PlacementPolicyMapper.java index f5bdaef..191a080 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/netmap/PlacementPolicyMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/netmap/PlacementPolicyMapper.java @@ -27,7 +27,7 @@ public class PlacementPolicyMapper { } public static PlacementPolicy toModel(Types.PlacementPolicy placementPolicy) { - if (isNull(placementPolicy)) { + if (isNull(placementPolicy) || placementPolicy.getSerializedSize() == 0) { return null; } diff --git a/models/src/main/java/info/frostfs/sdk/mappers/netmap/ReplicaMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/netmap/ReplicaMapper.java index e25ec21..6bb3939 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/netmap/ReplicaMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/netmap/ReplicaMapper.java @@ -22,7 +22,7 @@ public class ReplicaMapper { } public static Replica toModel(Types.Replica replica) { - if (isNull(replica)) { + if (isNull(replica) || replica.getSerializedSize() == 0) { return null; } diff --git a/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectAttributeMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectAttributeMapper.java index 17c3e94..687aa86 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectAttributeMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectAttributeMapper.java @@ -22,7 +22,7 @@ public class ObjectAttributeMapper { } public static ObjectAttribute toModel(Types.Header.Attribute attribute) { - if (isNull(attribute)) { + if (isNull(attribute) || attribute.getSerializedSize() == 0) { return null; } diff --git a/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectFilterMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectFilterMapper.java index b1c121d..874e9b1 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectFilterMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectFilterMapper.java @@ -7,6 +7,7 @@ import info.frostfs.sdk.dto.object.ObjectFilter; import static java.util.Objects.isNull; public class ObjectFilterMapper { + private static final String ERROR_UNKNOWN_VALUE_TEMPLATE = "Unknown MatchType. Value: %s."; private ObjectFilterMapper() { } @@ -19,7 +20,7 @@ public class ObjectFilterMapper { var objectMatchType = Types.MatchType.forNumber(filter.getMatchType().value); if (isNull(objectMatchType)) { throw new IllegalArgumentException( - String.format("Unknown MatchType. Value: %s.", filter.getMatchType().name()) + String.format(ERROR_UNKNOWN_VALUE_TEMPLATE, filter.getMatchType().name()) ); } diff --git a/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectFrostFSMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectFrostFSMapper.java index aa5f698..228661b 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectFrostFSMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectFrostFSMapper.java @@ -12,7 +12,7 @@ public class ObjectFrostFSMapper { } public static ObjectFrostFS toModel(Types.Object object) { - if (isNull(object)) { + if (isNull(object) || object.getSerializedSize() == 0) { return null; } diff --git a/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectHeaderMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectHeaderMapper.java index fc5b4cc..af8b898 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectHeaderMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectHeaderMapper.java @@ -7,12 +7,14 @@ import info.frostfs.sdk.dto.object.ObjectHeader; import info.frostfs.sdk.enums.ObjectType; import info.frostfs.sdk.mappers.VersionMapper; import info.frostfs.sdk.mappers.container.ContainerIdMapper; +import org.apache.commons.collections4.ListUtils; import java.util.stream.Collectors; import static java.util.Objects.isNull; public class ObjectHeaderMapper { + private static final String ERROR_UNKNOWN_VALUE_TEMPLATE = "Unknown ObjectType. Value: %s."; private ObjectHeaderMapper() { } @@ -25,7 +27,7 @@ public class ObjectHeaderMapper { var objectType = Types.ObjectType.forNumber(header.getObjectType().value); if (isNull(objectType)) { throw new IllegalArgumentException( - String.format("Unknown ObjectType. Value: %s.", header.getObjectType().name()) + String.format(ERROR_UNKNOWN_VALUE_TEMPLATE, header.getObjectType().name()) ); } @@ -41,21 +43,23 @@ public class ObjectHeaderMapper { } public static ObjectHeader toModel(Types.Header header) { - if (isNull(header)) { + if (isNull(header) || header.getSerializedSize() == 0) { return null; } var objectType = ObjectType.get(header.getObjectTypeValue()); if (isNull(objectType)) { throw new IllegalArgumentException( - String.format("Unknown ObjectType. Value: %s.", header.getObjectType()) + String.format(ERROR_UNKNOWN_VALUE_TEMPLATE, header.getObjectType()) ); } return new ObjectHeader( new ContainerId(header.getContainerId().getValue().toByteArray()), objectType, - header.getAttributesList().stream().map(ObjectAttributeMapper::toModel).collect(Collectors.toList()), + ListUtils.emptyIfNull(header.getAttributesList()).stream() + .map(ObjectAttributeMapper::toModel) + .collect(Collectors.toList()), header.getPayloadLength(), VersionMapper.toModel(header.getVersion()) ); diff --git a/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectIdMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectIdMapper.java index af51095..5c05d17 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectIdMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectIdMapper.java @@ -22,6 +22,10 @@ public class ObjectIdMapper { } public static ObjectId toModel(Types.ObjectID objectId) { + if (isNull(objectId) || objectId.getSerializedSize() == 0) { + return null; + } + return new ObjectId(objectId.getValue().toByteArray()); } } diff --git a/models/src/test/java/info/frostfs/sdk/UuidExtensionTest.java b/models/src/test/java/info/frostfs/sdk/UuidExtensionTest.java new file mode 100644 index 0000000..6cc40e0 --- /dev/null +++ b/models/src/test/java/info/frostfs/sdk/UuidExtensionTest.java @@ -0,0 +1,57 @@ +package info.frostfs.sdk; + +import org.junit.jupiter.api.Test; + +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class UuidExtensionTest { + private static final UUID TEST_UUID = UUID.fromString("ca70c669-0294-432d-8b83-05fc1475a579"); + private static final byte[] TEST_UUID_BYTES = + new byte[]{-54, 112, -58, 105, 2, -108, 67, 45, -117, -125, 5, -4, 20, 117, -91, 121}; + + @Test + void uuidAsBytes_success() { + //When + var result = UuidExtension.asBytes(TEST_UUID); + + //Then + assertThat(result).hasSize(16).containsExactly(TEST_UUID_BYTES); + } + + @Test + void uuidAsBytes_givenParamsIsNull() { + //When + Then + assertThrows(IllegalArgumentException.class, () -> UuidExtension.asBytes(null)); + } + + @Test + void bytesAsUuid_success() { + //When + var result = UuidExtension.asUuid(TEST_UUID_BYTES); + + //Then + assertEquals(TEST_UUID, result); + } + + @Test + void bytesAsUuid_givenParamsIsNull() { + //When + Then + assertThrows(IllegalArgumentException.class, () -> UuidExtension.asUuid(null)); + } + + @Test + void bytesAsUuid_wrongArraySize() { + //Given + var valueLength15 = new byte[]{-54, 112, -58, 105, 2, -108, 67, 45, -117, -125, 5, -4, 20, 117, -91}; + var valueLength17 = new byte[]{-54, 112, -58, 105, 2, -108, 67, 45, -117, -125, 5, -4, 20, 117, -91, 121, 12}; + + + //When + Then + assertThrows(IllegalArgumentException.class, () -> UuidExtension.asUuid(valueLength15)); + assertThrows(IllegalArgumentException.class, () -> UuidExtension.asUuid(valueLength17)); + } +} diff --git a/models/src/test/java/info/frostfs/sdk/mappers/MetaHeaderMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/MetaHeaderMapperTest.java new file mode 100644 index 0000000..7d874cc --- /dev/null +++ b/models/src/test/java/info/frostfs/sdk/mappers/MetaHeaderMapperTest.java @@ -0,0 +1,33 @@ +package info.frostfs.sdk.mappers; + +import info.frostfs.sdk.dto.MetaHeader; +import info.frostfs.sdk.dto.Version; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class MetaHeaderMapperTest { + + @Test + void toGrpcMessage_success() { + //Given + var version = new Version(1, 2); + var metaHeader = new MetaHeader(version, 1, 2); + + //When + var result = MetaHeaderMapper.toGrpcMessage(metaHeader); + + //Then + assertNotNull(result); + assertEquals(metaHeader.getEpoch(), result.getEpoch()); + assertEquals(metaHeader.getTtl(), result.getTtl()); + assertEquals(metaHeader.getVersion().getMajor(), result.getVersion().getMajor()); + assertEquals(metaHeader.getVersion().getMinor(), result.getVersion().getMinor()); + } + + @Test + void toGrpcMessage_null() { + //When + Then + assertNull(MetaHeaderMapper.toGrpcMessage(null)); + } +} diff --git a/models/src/test/java/info/frostfs/sdk/mappers/OwnerIdMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/OwnerIdMapperTest.java new file mode 100644 index 0000000..bc51895 --- /dev/null +++ b/models/src/test/java/info/frostfs/sdk/mappers/OwnerIdMapperTest.java @@ -0,0 +1,35 @@ +package info.frostfs.sdk.mappers; + +import info.frostfs.sdk.dto.OwnerId; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class OwnerIdMapperTest { + private static final String OWNER_ID = "NVxUSpEEJzYXZZtUs18PrJTD9QZkLLNQ8S"; + + @Test + void toGrpcMessage_success() { + //Given + var ownerId = new OwnerId(OWNER_ID); + var expected = new byte[]{ + 53, 110, 42, -125, -76, -25, -44, -94, 22, -98, 117, -100, -5, 103, 74, -128, -51, 37, -116, -102, 71, + -1, 95, -4, 3 + }; + + //When + var result = OwnerIdMapper.toGrpcMessage(ownerId); + + //Then + assertNotNull(result); + assertThat(result.getValue().toByteArray()).hasSize(25).containsExactly(expected); + } + + @Test + void toGrpcMessage_null() { + //When + Then + assertNull(OwnerIdMapper.toGrpcMessage(null)); + } +} diff --git a/models/src/test/java/info/frostfs/sdk/mappers/SessionMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/SessionMapperTest.java new file mode 100644 index 0000000..f8c125e --- /dev/null +++ b/models/src/test/java/info/frostfs/sdk/mappers/SessionMapperTest.java @@ -0,0 +1,67 @@ +package info.frostfs.sdk.mappers; + +import com.google.protobuf.ByteString; +import frostfs.session.Types; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; + +public class SessionMapperTest { + private static final byte[] SERIALIZED_TOKEN = new byte[] + {10, 21, 10, 5, 1, 2, 3, 4, 5, 26, 6, 8, 1, 16, 3, 24, 2, 34, 4, 6, 7, 8, 9}; + + @Test + void serialize_success() { + //Given + var sessionToken = createSessionToken(); + + //When + var result = SessionMapper.serialize(sessionToken); + + //Then + assertNotNull(result); + assertThat(result).hasSize(23).containsExactly(SERIALIZED_TOKEN); + } + + @Test + void serialize_wrong() { + //When + Then + assertThrows(IllegalArgumentException.class, () -> SessionMapper.serialize(null)); + } + + @Test + void deserialize_success() { + //When + var result = SessionMapper.deserializeSessionToken(SERIALIZED_TOKEN); + + //Then + assertNotNull(result); + assertEquals(createSessionToken(), result); + } + + @Test + void deserialize_wrong() { + //When + Then + assertThrows(IllegalArgumentException.class, () -> SessionMapper.deserializeSessionToken(null)); + assertThrows(IllegalArgumentException.class, () -> SessionMapper.deserializeSessionToken(new byte[]{})); + } + + private Types.SessionToken createSessionToken() { + var lifetime = Types.SessionToken.Body.TokenLifetime.newBuilder() + .setExp(1) + .setIat(2) + .setNbf(3) + .build(); + + var body = Types.SessionToken.Body.newBuilder() + .setId(ByteString.copyFrom(new byte[]{1, 2, 3, 4, 5})) + .setSessionKey(ByteString.copyFrom(new byte[]{6, 7, 8, 9})) + .setLifetime(lifetime) + .build(); + + return Types.SessionToken.newBuilder() + .setBody(body) + .build(); + } +} diff --git a/models/src/test/java/info/frostfs/sdk/mappers/SignatureMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/SignatureMapperTest.java new file mode 100644 index 0000000..3435ab7 --- /dev/null +++ b/models/src/test/java/info/frostfs/sdk/mappers/SignatureMapperTest.java @@ -0,0 +1,56 @@ +package info.frostfs.sdk.mappers; + +import frostfs.refs.Types; +import info.frostfs.sdk.dto.Signature; +import info.frostfs.sdk.enums.SignatureScheme; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.mockito.MockedStatic; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mockStatic; + +public class SignatureMapperTest { + + @ParameterizedTest + @EnumSource(value = SignatureScheme.class) + void toGrpcMessage_success(SignatureScheme scheme) { + //Given + var signature = new Signature(); + signature.setKey(new byte[]{1, 2, 3}); + signature.setSign(new byte[]{4, 5, 6}); + signature.setScheme(scheme); + + //When + var result = SignatureMapper.toGrpcMessage(signature); + + //Then + assertNotNull(result); + assertThat(result.getKey().toByteArray()).containsExactly(signature.getKey()); + assertThat(result.getSign().toByteArray()).containsExactly(signature.getSign()); + assertEquals(signature.getScheme().value, result.getScheme().getNumber()); + } + + @Test + void toGrpcMessage_null() { + //When + Then + assertNull(SignatureMapper.toGrpcMessage(null)); + } + + @Test + void toGrpcMessage_notValidScheme() { + //Given + var signature = new Signature(); + signature.setScheme(SignatureScheme.ECDSA_SHA512); + + //When + Then + try (MockedStatic mockStatic = mockStatic(Types.SignatureScheme.class)) { + mockStatic.when(() -> Types.SignatureScheme.forNumber(signature.getScheme().value)) + .thenReturn(null); + + assertThrows(IllegalArgumentException.class, () -> SignatureMapper.toGrpcMessage(signature)); + } + } +} diff --git a/models/src/test/java/info/frostfs/sdk/mappers/StatusMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/StatusMapperTest.java new file mode 100644 index 0000000..d0f5a59 --- /dev/null +++ b/models/src/test/java/info/frostfs/sdk/mappers/StatusMapperTest.java @@ -0,0 +1,53 @@ +package info.frostfs.sdk.mappers; + +import frostfs.status.Types; +import info.frostfs.sdk.enums.StatusCode; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +import static org.junit.jupiter.api.Assertions.*; + +public class StatusMapperTest { + + @ParameterizedTest + @EnumSource(value = StatusCode.class) + void toModel_success(StatusCode statusCode) { + //Given + var status = Types.Status.newBuilder() + .setCode(statusCode.value) + .setMessage("test") + .build(); + + //When + var result = StatusMapper.toModel(status); + + //Then + assertNotNull(result); + assertEquals(status.getCode(), result.getCode().value); + assertEquals(status.getMessage(), result.getMessage()); + } + + @Test + void toModel_null() { + //When + var result = StatusMapper.toModel(null); + + //Then + assertNotNull(result); + assertEquals(StatusCode.SUCCESS.value, result.getCode().value); + assertEquals("", result.getMessage()); + } + + @Test + void toModel_notValid() { + //Given + var status = Types.Status.newBuilder() + .setCode(-1) + .setMessage("test") + .build(); + + //When + Then + assertThrows(IllegalArgumentException.class, () -> StatusMapper.toModel(status)); + } +} diff --git a/models/src/test/java/info/frostfs/sdk/mappers/VersionMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/VersionMapperTest.java new file mode 100644 index 0000000..3fea7b7 --- /dev/null +++ b/models/src/test/java/info/frostfs/sdk/mappers/VersionMapperTest.java @@ -0,0 +1,54 @@ +package info.frostfs.sdk.mappers; + +import frostfs.refs.Types; +import info.frostfs.sdk.dto.Version; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class VersionMapperTest { + + @Test + void toGrpcMessage_success() { + //Given + var version = new Version(1, 2); + + //When + var result = VersionMapper.toGrpcMessage(version); + + //Then + assertNotNull(result); + assertEquals(version.getMajor(), result.getMajor()); + assertEquals(version.getMinor(), result.getMinor()); + } + + @Test + void toGrpcMessage_null() { + //When + Then + assertNull(VersionMapper.toGrpcMessage(null)); + } + + @Test + void toModel_success() { + //Given + var version = Types.Version.newBuilder() + .setMajor(1) + .setMinor(2) + .build(); + + //When + var result = VersionMapper.toModel(version); + + //Then + assertNotNull(result); + assertEquals(version.getMajor(), result.getMajor()); + assertEquals(version.getMinor(), result.getMinor()); + } + + @Test + void toModel_null() { + //When + Then + assertNull(VersionMapper.toModel(null)); + assertNull(VersionMapper.toModel(Types.Version.getDefaultInstance())); + } +} diff --git a/models/src/test/java/info/frostfs/sdk/mappers/container/ContainerIdMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/container/ContainerIdMapperTest.java new file mode 100644 index 0000000..a421415 --- /dev/null +++ b/models/src/test/java/info/frostfs/sdk/mappers/container/ContainerIdMapperTest.java @@ -0,0 +1,28 @@ +package info.frostfs.sdk.mappers.container; + +import info.frostfs.sdk.dto.container.ContainerId; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class ContainerIdMapperTest { + + @Test + void toGrpcMessage_success() { + //Given + var containerId = new ContainerId("EQGx2QeYHJb53uRwYGzcQaW191sZpdNrjutk6veUSV2R"); + + //When + var result = ContainerIdMapper.toGrpcMessage(containerId); + + //Then + assertNotNull(result); + assertEquals(containerId.getValue(), new ContainerId(result.getValue().toByteArray()).getValue()); + } + + @Test + void toGrpcMessage_null() { + //When + Then + assertNull(ContainerIdMapper.toGrpcMessage(null)); + } +} diff --git a/models/src/test/java/info/frostfs/sdk/mappers/container/ContainerMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/container/ContainerMapperTest.java new file mode 100644 index 0000000..7db4345 --- /dev/null +++ b/models/src/test/java/info/frostfs/sdk/mappers/container/ContainerMapperTest.java @@ -0,0 +1,116 @@ +package info.frostfs.sdk.mappers.container; + +import com.google.protobuf.ByteString; +import frostfs.container.Types; +import info.frostfs.sdk.dto.container.Container; +import info.frostfs.sdk.dto.netmap.PlacementPolicy; +import info.frostfs.sdk.dto.netmap.Replica; +import info.frostfs.sdk.enums.BasicAcl; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +import java.util.UUID; + +import static info.frostfs.sdk.UuidExtension.asBytes; +import static info.frostfs.sdk.UuidExtension.asUuid; +import static org.junit.jupiter.api.Assertions.*; + +public class ContainerMapperTest { + + @Test + void toGrpcMessage_success() { + //Given + var placementPolicy = new PlacementPolicy(true, new Replica[]{new Replica(1)}); + var container = new Container(BasicAcl.PUBLIC_RW, placementPolicy); + + //When + var result = ContainerMapper.toGrpcMessage(container); + + //Then + assertNotNull(result); + assertEquals(container.getBasicAcl().value, result.getBasicAcl()); + assertEquals(container.getNonce(), asUuid(result.getNonce().toByteArray())); + assertEquals(container.getPlacementPolicy().isUnique(), result.getPlacementPolicy().getUnique()); + assertEquals(placementPolicy.getReplicas().length, result.getPlacementPolicy().getReplicasCount()); + assertEquals( + container.getPlacementPolicy().getReplicas()[0].getCount(), + result.getPlacementPolicy().getReplicasList().get(0).getCount() + ); + assertEquals( + container.getPlacementPolicy().getReplicas()[0].getSelector(), + result.getPlacementPolicy().getReplicasList().get(0).getSelector() + ); + } + + @Test + void toGrpcMessage_null() { + //When + Then + assertNull(ContainerMapper.toGrpcMessage(null)); + } + + @ParameterizedTest + @EnumSource(value = BasicAcl.class) + void toModel_success(BasicAcl basicAcl) { + //Given + var version = frostfs.refs.Types.Version.newBuilder() + .setMajor(1) + .setMinor(2) + .build(); + + var replica = frostfs.netmap.Types.Replica.newBuilder() + .setCount(1) + .setSelector("") + .build(); + + var placementPolicy = frostfs.netmap.Types.PlacementPolicy.newBuilder() + .setUnique(true) + .addReplicas(replica) + .build(); + + var container = Types.Container.newBuilder() + .setBasicAcl(basicAcl.value) + .setNonce(ByteString.copyFrom(asBytes(UUID.randomUUID()))) + .setVersion(version) + .setPlacementPolicy(placementPolicy) + .build(); + + //When + var result = ContainerMapper.toModel(container); + + //Then + assertNotNull(result); + assertEquals(container.getBasicAcl(), result.getBasicAcl().value); + assertEquals(asUuid(container.getNonce().toByteArray()), result.getNonce()); + assertEquals(container.getPlacementPolicy().getUnique(), result.getPlacementPolicy().isUnique()); + assertEquals(placementPolicy.getReplicasCount(), result.getPlacementPolicy().getReplicas().length); + assertEquals( + container.getPlacementPolicy().getReplicasList().get(0).getCount(), + result.getPlacementPolicy().getReplicas()[0].getCount() + ); + assertEquals( + container.getPlacementPolicy().getReplicasList().get(0).getSelector(), + result.getPlacementPolicy().getReplicas()[0].getSelector() + ); + assertEquals(version.getMajor(), result.getVersion().getMajor()); + assertEquals(version.getMinor(), result.getVersion().getMinor()); + } + + @Test + void toModel_null() { + //When + Then + assertNull(ContainerMapper.toModel(null)); + assertNull(ContainerMapper.toModel(Types.Container.getDefaultInstance())); + } + + @Test + void toModel_notValid() { + //Given + var container = Types.Container.newBuilder() + .setBasicAcl(-1) + .build(); + + //When + Then + assertThrows(IllegalArgumentException.class, () -> ContainerMapper.toModel(container)); + } +} diff --git a/models/src/test/java/info/frostfs/sdk/mappers/netmap/NetmapSnapshotMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/netmap/NetmapSnapshotMapperTest.java new file mode 100644 index 0000000..731eb51 --- /dev/null +++ b/models/src/test/java/info/frostfs/sdk/mappers/netmap/NetmapSnapshotMapperTest.java @@ -0,0 +1,98 @@ +package info.frostfs.sdk.mappers.netmap; + +import com.google.protobuf.ByteString; +import frostfs.netmap.Service; +import frostfs.netmap.Types; +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; + +public class NetmapSnapshotMapperTest { + + @Test + void toModel_success() { + //Given + var version = frostfs.refs.Types.Version.newBuilder() + .setMajor(1) + .setMinor(2) + .build(); + + var nodeInfo1 = Types.NodeInfo.newBuilder() + .setPublicKey(ByteString.copyFrom(new byte[]{1, 2, 3, 4, 5, 6})) + .addAttributes(Types.NodeInfo.Attribute.newBuilder().setKey("key1").setValue("value1").build()) + .addAttributes(Types.NodeInfo.Attribute.newBuilder().setKey("key2").setValue("value2").build()) + .addAddresses("address1") + .addAddresses("address2") + .setState(Types.NodeInfo.State.ONLINE) + .build(); + + var nodeInfo2 = Types.NodeInfo.newBuilder() + .setPublicKey(ByteString.copyFrom(new byte[]{7, 8, 9, 10})) + .addAttributes(Types.NodeInfo.Attribute.newBuilder().setKey("key3").setValue("value3").build()) + .addAttributes(Types.NodeInfo.Attribute.newBuilder().setKey("key4").setValue("value4").build()) + .addAddresses("address3") + .addAddresses("address4") + .setState(Types.NodeInfo.State.MAINTENANCE) + .build(); + + var netmap = Types.Netmap.newBuilder() + .setEpoch(10) + .addNodes(nodeInfo1) + .addNodes(nodeInfo2) + .build(); + + var body = Service.NetmapSnapshotResponse.Body.newBuilder() + .setNetmap(netmap) + .build(); + + var response = Service.NetmapSnapshotResponse.newBuilder() + .setBody(body) + .setMetaHeader(frostfs.session.Types.ResponseMetaHeader.newBuilder().setVersion(version).build()) + .build(); + + //When + var result = NetmapSnapshotMapper.toModel(response); + + //Then + assertNotNull(result); + + var resultNode1 = result.getNodeInfoCollection().get(0); + var resultNode2 = result.getNodeInfoCollection().get(1); + + + assertEquals(netmap.getEpoch(), result.getEpoch()); + assertEquals(netmap.getNodesCount(), result.getNodeInfoCollection().size()); + + assertThat(resultNode1.getPublicKey()).containsExactly(nodeInfo1.getPublicKey().toByteArray()); + assertEquals(nodeInfo1.getState().getNumber(), resultNode1.getState().value); + assertEquals(version.getMajor(), resultNode1.getVersion().getMajor()); + assertEquals(version.getMinor(), resultNode1.getVersion().getMinor()); + assertThat(resultNode1.getAddresses()) + .hasSize(2) + .containsAll(nodeInfo1.getAddressesList()); + assertThat(resultNode1.getAttributes()) + .hasSize(2) + .contains(Map.entry("key1", "value1"), Map.entry("key2", "value2")); + + assertThat(resultNode2.getPublicKey()).containsExactly(nodeInfo2.getPublicKey().toByteArray()); + assertEquals(nodeInfo2.getState().getNumber(), resultNode2.getState().value); + assertEquals(version.getMajor(), resultNode2.getVersion().getMajor()); + assertEquals(version.getMinor(), resultNode2.getVersion().getMinor()); + assertThat(resultNode2.getAddresses()) + .hasSize(2) + .containsAll(nodeInfo2.getAddressesList()); + assertThat(resultNode2.getAttributes()) + .hasSize(2) + .contains(Map.entry("key3", "value3"), Map.entry("key4", "value4")); + } + + @Test + void toModel_null() { + //When + Then + assertNull(NetmapSnapshotMapper.toModel(null)); + assertNull(NetmapSnapshotMapper.toModel(Service.NetmapSnapshotResponse.getDefaultInstance())); + } +} diff --git a/models/src/test/java/info/frostfs/sdk/mappers/netmap/NodeInfoMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/netmap/NodeInfoMapperTest.java new file mode 100644 index 0000000..c60fceb --- /dev/null +++ b/models/src/test/java/info/frostfs/sdk/mappers/netmap/NodeInfoMapperTest.java @@ -0,0 +1,123 @@ +package info.frostfs.sdk.mappers.netmap; + +import com.google.protobuf.ByteString; +import frostfs.netmap.Service; +import frostfs.netmap.Types; +import info.frostfs.sdk.enums.NodeState; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.mockito.MockedStatic; + +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mockStatic; + +public class NodeInfoMapperTest { + + @ParameterizedTest + @EnumSource(value = Types.NodeInfo.State.class, names = "UNRECOGNIZED", mode = EnumSource.Mode.EXCLUDE) + void toModel_success(Types.NodeInfo.State state) { + //Given + var version = frostfs.refs.Types.Version.newBuilder() + .setMajor(1) + .setMinor(2) + .build(); + + var nodeInfo = Types.NodeInfo.newBuilder() + .setPublicKey(ByteString.copyFrom(new byte[]{1, 2, 3, 4, 5, 6})) + .addAttributes(Types.NodeInfo.Attribute.newBuilder().setKey("key1").setValue("value1").build()) + .addAttributes(Types.NodeInfo.Attribute.newBuilder().setKey("key2").setValue("value2").build()) + .addAddresses("address1") + .addAddresses("address2") + .setState(state) + .build(); + + var body = Service.LocalNodeInfoResponse.Body.newBuilder() + .setNodeInfo(nodeInfo) + .setVersion(version) + .build(); + + //When + var result = NodeInfoMapper.toModel(body); + + //Then + assertNotNull(result); + assertThat(result.getPublicKey()).containsExactly(nodeInfo.getPublicKey().toByteArray()); + assertEquals(nodeInfo.getState().getNumber(), result.getState().value); + assertEquals(version.getMajor(), result.getVersion().getMajor()); + assertEquals(version.getMinor(), result.getVersion().getMinor()); + assertThat(result.getAddresses()) + .hasSize(2) + .containsAll(nodeInfo.getAddressesList()); + assertThat(result.getAttributes()) + .hasSize(2) + .contains(Map.entry("key1", "value1"), Map.entry("key2", "value2")); + } + + @ParameterizedTest + @EnumSource(value = Types.NodeInfo.State.class, names = "UNRECOGNIZED", mode = EnumSource.Mode.EXCLUDE) + void toModel2_success(Types.NodeInfo.State state) { + //Given + var version = frostfs.refs.Types.Version.newBuilder() + .setMajor(1) + .setMinor(2) + .build(); + + var nodeInfo = Types.NodeInfo.newBuilder() + .setPublicKey(ByteString.copyFrom(new byte[]{1, 2, 3, 4, 5, 6})) + .addAttributes(Types.NodeInfo.Attribute.newBuilder().setKey("key1").setValue("value1").build()) + .addAttributes(Types.NodeInfo.Attribute.newBuilder().setKey("key2").setValue("value2").build()) + .addAddresses("address1") + .addAddresses("address2") + .setState(state) + .build(); + + //When + var result = NodeInfoMapper.toModel(nodeInfo, version); + + //Then + assertNotNull(result); + assertThat(result.getPublicKey()).containsExactly(nodeInfo.getPublicKey().toByteArray()); + assertEquals(nodeInfo.getState().getNumber(), result.getState().value); + assertEquals(version.getMajor(), result.getVersion().getMajor()); + assertEquals(version.getMinor(), result.getVersion().getMinor()); + assertThat(result.getAddresses()) + .hasSize(2) + .containsAll(nodeInfo.getAddressesList()); + assertThat(result.getAttributes()) + .hasSize(2) + .contains(Map.entry("key1", "value1"), Map.entry("key2", "value2")); + } + + @Test + void toModel_null() { + //When + Then + assertNull(NodeInfoMapper.toModel(null)); + assertNull(NodeInfoMapper.toModel(null, null)); + assertNull(NodeInfoMapper.toModel(Service.LocalNodeInfoResponse.Body.getDefaultInstance())); + assertNull(NodeInfoMapper.toModel(Types.NodeInfo.getDefaultInstance(), null)); + } + + @Test + void toModel_notValidScheme() { + //Given + var nodeInfo = Types.NodeInfo.newBuilder() + .setState(Types.NodeInfo.State.ONLINE) + .build(); + + var body = Service.LocalNodeInfoResponse.Body.newBuilder() + .setNodeInfo(nodeInfo) + .build(); + + //When + Then + try (MockedStatic mockStatic = mockStatic(NodeState.class)) { + mockStatic.when(() -> NodeState.get(Types.NodeInfo.State.ONLINE.getNumber())) + .thenReturn(null); + + assertThrows(IllegalArgumentException.class, () -> NodeInfoMapper.toModel(body)); + } + } +} diff --git a/models/src/test/java/info/frostfs/sdk/mappers/netmap/PlacementPolicyMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/netmap/PlacementPolicyMapperTest.java new file mode 100644 index 0000000..3cc57ae --- /dev/null +++ b/models/src/test/java/info/frostfs/sdk/mappers/netmap/PlacementPolicyMapperTest.java @@ -0,0 +1,77 @@ +package info.frostfs.sdk.mappers.netmap; + +import frostfs.netmap.Types; +import info.frostfs.sdk.dto.netmap.PlacementPolicy; +import info.frostfs.sdk.dto.netmap.Replica; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class PlacementPolicyMapperTest { + + @Test + void toGrpcMessage_success() { + //Given + var replica1 = new Replica(1, "test1"); + var replica2 = new Replica(2, "test2"); + + var placementPolicy = new PlacementPolicy(true, new Replica[]{replica1, replica2}); + + //When + var result = PlacementPolicyMapper.toGrpcMessage(placementPolicy); + + //Then + assertNotNull(result); + assertEquals(placementPolicy.isUnique(), result.getUnique()); + assertEquals(placementPolicy.getReplicas().length, result.getReplicasCount()); + assertEquals(replica1.getCount(), result.getReplicas(0).getCount()); + assertEquals(replica1.getSelector(), result.getReplicas(0).getSelector()); + assertEquals(replica2.getCount(), result.getReplicas(1).getCount()); + assertEquals(replica2.getSelector(), result.getReplicas(1).getSelector()); + } + + @Test + void toGrpcMessage_null() { + //When + Then + assertNull(PlacementPolicyMapper.toGrpcMessage(null)); + } + + @Test + void toModel_success() { + //Given + var replica1 = Types.Replica.newBuilder() + .setCount(1) + .setSelector("test1") + .build(); + + var replica2 = Types.Replica.newBuilder() + .setCount(2) + .setSelector("test2") + .build(); + + var placementPolicy = Types.PlacementPolicy.newBuilder() + .setUnique(true) + .addReplicas(replica1) + .addReplicas(replica2) + .build(); + + //When + var result = PlacementPolicyMapper.toModel(placementPolicy); + + //Then + assertNotNull(result); + assertEquals(placementPolicy.getUnique(), result.isUnique()); + assertEquals(placementPolicy.getReplicasCount(), result.getReplicas().length); + assertEquals(replica1.getCount(), result.getReplicas()[0].getCount()); + assertEquals(replica1.getSelector(), result.getReplicas()[0].getSelector()); + assertEquals(replica2.getCount(), result.getReplicas()[1].getCount()); + assertEquals(replica2.getSelector(), result.getReplicas()[1].getSelector()); + } + + @Test + void toModel_null() { + //When + Then + assertNull(PlacementPolicyMapper.toModel(null)); + assertNull(PlacementPolicyMapper.toModel(Types.PlacementPolicy.getDefaultInstance())); + } +} diff --git a/models/src/test/java/info/frostfs/sdk/mappers/netmap/ReplicaMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/netmap/ReplicaMapperTest.java new file mode 100644 index 0000000..973e74c --- /dev/null +++ b/models/src/test/java/info/frostfs/sdk/mappers/netmap/ReplicaMapperTest.java @@ -0,0 +1,54 @@ +package info.frostfs.sdk.mappers.netmap; + +import frostfs.netmap.Types; +import info.frostfs.sdk.dto.netmap.Replica; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class ReplicaMapperTest { + + @Test + void toGrpcMessage_success() { + //Given + var replica = new Replica(1); + + //When + var result = ReplicaMapper.toGrpcMessage(replica); + + //Then + assertNotNull(result); + assertEquals(replica.getCount(), result.getCount()); + assertEquals(replica.getSelector(), result.getSelector()); + } + + @Test + void toGrpcMessage_null() { + //When + Then + assertNull(ReplicaMapper.toGrpcMessage(null)); + } + + @Test + void toModel_success() { + //Given + var replica = Types.Replica.newBuilder() + .setCount(1) + .setSelector("") + .build(); + + //When + var result = ReplicaMapper.toModel(replica); + + //Then + assertNotNull(result); + assertEquals(replica.getCount(), result.getCount()); + assertEquals(replica.getSelector(), result.getSelector()); + } + + @Test + void toModel_null() { + //When + Then + assertNull(ReplicaMapper.toModel(null)); + assertNull(ReplicaMapper.toModel(Types.Replica.getDefaultInstance())); + } +} diff --git a/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectAttributeMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectAttributeMapperTest.java new file mode 100644 index 0000000..4ae2902 --- /dev/null +++ b/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectAttributeMapperTest.java @@ -0,0 +1,54 @@ +package info.frostfs.sdk.mappers.object; + +import frostfs.object.Types; +import info.frostfs.sdk.dto.object.ObjectAttribute; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class ObjectAttributeMapperTest { + + @Test + void toGrpcMessage_success() { + //Given + var objectAttribute = new ObjectAttribute("key", "value"); + + //When + var result = ObjectAttributeMapper.toGrpcMessage(objectAttribute); + + //Then + assertNotNull(result); + assertEquals(objectAttribute.getKey(), result.getKey()); + assertEquals(objectAttribute.getValue(), result.getValue()); + } + + @Test + void toGrpcMessage_null() { + //When + Then + assertNull(ObjectAttributeMapper.toGrpcMessage(null)); + } + + @Test + void toModel_success() { + //Given + var objectAttribute = Types.Header.Attribute.newBuilder() + .setKey("key") + .setValue("value") + .build(); + + //When + var result = ObjectAttributeMapper.toModel(objectAttribute); + + //Then + assertNotNull(result); + assertEquals(objectAttribute.getKey(), result.getKey()); + assertEquals(objectAttribute.getValue(), result.getValue()); + } + + @Test + void toModel_null() { + //When + Then + assertNull(ObjectAttributeMapper.toModel(null)); + assertNull(ObjectAttributeMapper.toModel(Types.Header.Attribute.getDefaultInstance())); + } +} diff --git a/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectFilterMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectFilterMapperTest.java new file mode 100644 index 0000000..3451ccc --- /dev/null +++ b/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectFilterMapperTest.java @@ -0,0 +1,53 @@ +package info.frostfs.sdk.mappers.object; + +import frostfs.object.Types; +import info.frostfs.sdk.dto.object.ObjectFilter; +import info.frostfs.sdk.enums.ObjectMatchType; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.mockito.MockedStatic; + +import static info.frostfs.sdk.enums.ObjectMatchType.UNSPECIFIED; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mockStatic; + +public class ObjectFilterMapperTest { + + @ParameterizedTest + @EnumSource(value = ObjectMatchType.class) + void toGrpcMessage_success(ObjectMatchType type) { + //Given + var objectFilter = new ObjectFilter(type, "key", "value"); + + //When + var result = ObjectFilterMapper.toGrpcMessage(objectFilter); + + //Then + assertNotNull(result); + assertEquals(objectFilter.getKey(), result.getKey()); + assertEquals(objectFilter.getValue(), result.getValue()); + assertEquals(objectFilter.getMatchType().value, result.getMatchType().getNumber()); + + } + + @Test + void toGrpcMessage_null() { + //When + Then + assertNull(ObjectFilterMapper.toGrpcMessage(null)); + } + + @Test + void toGrpcMessage_notValidScheme() { + //Given + var objectFilter = new ObjectFilter(UNSPECIFIED, "key", "value"); + + //When + Then + try (MockedStatic mockStatic = mockStatic(Types.MatchType.class)) { + mockStatic.when(() -> Types.MatchType.forNumber(objectFilter.getMatchType().value)) + .thenReturn(null); + + assertThrows(IllegalArgumentException.class, () -> ObjectFilterMapper.toGrpcMessage(objectFilter)); + } + } +} diff --git a/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectFrostFSMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectFrostFSMapperTest.java new file mode 100644 index 0000000..dbcf9b7 --- /dev/null +++ b/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectFrostFSMapperTest.java @@ -0,0 +1,60 @@ +package info.frostfs.sdk.mappers.object; + +import com.google.protobuf.ByteString; +import frostfs.object.Types; +import info.frostfs.sdk.dto.container.ContainerId; +import info.frostfs.sdk.dto.object.ObjectId; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; + +public class ObjectFrostFSMapperTest { + @Test + void toModel_success() { + //Given + var containerId = new ContainerId("EQGx2QeYHJb53uRwYGzcQaW191sZpdNrjutk6veUSV2R"); + var cid = frostfs.refs.Types.ContainerID.newBuilder() + .setValue(ByteString.copyFrom(containerId.toHash())) + .build(); + var version = frostfs.refs.Types.Version.newBuilder() + .setMajor(1) + .setMinor(2) + .build(); + var header = Types.Header.newBuilder() + .setObjectType(Types.ObjectType.REGULAR) + .setVersion(version) + .setContainerId(cid) + .setPayloadLength(10) + .build(); + + var objectId = new ObjectId("85orCLKSu3X1jGiTFmwmTUsBU88RBARNwuRwrEy5pyww"); + var object = Types.Object.newBuilder() + .setObjectId(ObjectIdMapper.toGrpcMessage(objectId)) + .setPayload(ByteString.copyFrom(new byte[]{1, 2, 3, 4, 5})) + .setHeader(header) + .build(); + + //When + var result = ObjectFrostFSMapper.toModel(object); + + //Then + assertNotNull(result); + assertEquals(objectId.getValue(), result.getObjectId().getValue()); + assertThat(result.getPayload()).containsExactly(object.getPayload().toByteArray()); + assertThat(result.getHeader().getAttributes()).hasSize(0); + assertEquals(header.getObjectTypeValue(), result.getHeader().getObjectType().value); + assertEquals(version.getMajor(), result.getHeader().getVersion().getMajor()); + assertEquals(version.getMinor(), result.getHeader().getVersion().getMinor()); + assertEquals(header.getPayloadLength(), result.getHeader().getPayloadLength()); + assertEquals(containerId.getValue(), result.getHeader().getContainerId().getValue()); + } + + @Test + void toModel_null() { + //When + Then + assertNull(ObjectFrostFSMapper.toModel(null)); + assertNull(ObjectFrostFSMapper.toModel(Types.Object.getDefaultInstance())); + } + +} diff --git a/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectHeaderMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectHeaderMapperTest.java new file mode 100644 index 0000000..dd410b2 --- /dev/null +++ b/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectHeaderMapperTest.java @@ -0,0 +1,127 @@ +package info.frostfs.sdk.mappers.object; + +import com.google.protobuf.ByteString; +import frostfs.object.Types; +import info.frostfs.sdk.dto.container.ContainerId; +import info.frostfs.sdk.dto.object.ObjectAttribute; +import info.frostfs.sdk.dto.object.ObjectHeader; +import info.frostfs.sdk.enums.ObjectType; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.mockito.MockedStatic; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mockStatic; + +public class ObjectHeaderMapperTest { + + @ParameterizedTest + @EnumSource(value = ObjectType.class) + void toGrpcMessage_success(ObjectType type) { + //Given + var containerId = new ContainerId("EQGx2QeYHJb53uRwYGzcQaW191sZpdNrjutk6veUSV2R"); + var objectHeader = new ObjectHeader( + containerId, + type, + List.of(new ObjectAttribute("Filename", "cat.jpg"), new ObjectAttribute("Filename2", "cat2.jpg")) + ); + + //When + var result = ObjectHeaderMapper.toGrpcMessage(objectHeader); + + //Then + assertNotNull(result); + assertEquals( + objectHeader.getContainerId().getValue(), + new ContainerId(result.getContainerId().getValue().toByteArray()).getValue() + ); + assertEquals(objectHeader.getObjectType().value, result.getObjectType().getNumber()); + assertEquals(objectHeader.getAttributes().size(), result.getAttributesCount()); + + assertEquals(objectHeader.getAttributes().get(0).getKey(), result.getAttributes(0).getKey()); + assertEquals(objectHeader.getAttributes().get(0).getValue(), result.getAttributes(0).getValue()); + + assertEquals(objectHeader.getAttributes().get(1).getKey(), result.getAttributes(1).getKey()); + assertEquals(objectHeader.getAttributes().get(1).getValue(), result.getAttributes(1).getValue()); + } + + @Test + void toGrpcMessage_null() { + //When + Then + assertNull(ObjectHeaderMapper.toGrpcMessage(null)); + } + + @Test + void toGrpcMessage_notValidScheme() { + //Given + var containerId = new ContainerId("EQGx2QeYHJb53uRwYGzcQaW191sZpdNrjutk6veUSV2R"); + var objectHeader = new ObjectHeader(containerId, ObjectType.REGULAR, null); + + //When + Then + try (MockedStatic mockStatic = mockStatic(Types.ObjectType.class)) { + mockStatic.when(() -> Types.ObjectType.forNumber(objectHeader.getObjectType().value)) + .thenReturn(null); + + assertThrows(IllegalArgumentException.class, () -> ObjectHeaderMapper.toGrpcMessage(objectHeader)); + } + } + + @ParameterizedTest + @EnumSource(value = Types.ObjectType.class, names = "UNRECOGNIZED", mode = EnumSource.Mode.EXCLUDE) + void toModel_success(Types.ObjectType type) { + //Given + var containerId = new ContainerId("EQGx2QeYHJb53uRwYGzcQaW191sZpdNrjutk6veUSV2R"); + var cid = frostfs.refs.Types.ContainerID.newBuilder() + .setValue(ByteString.copyFrom(containerId.toHash())) + .build(); + var version = frostfs.refs.Types.Version.newBuilder() + .setMajor(1) + .setMinor(2) + .build(); + var header = Types.Header.newBuilder() + .setObjectType(type) + .setVersion(version) + .setContainerId(cid) + .setPayloadLength(10) + .build(); + + //When + var result = ObjectHeaderMapper.toModel(header); + + //Then + assertNotNull(result); + assertThat(result.getAttributes()).hasSize(0); + assertEquals(header.getObjectTypeValue(), result.getObjectType().value); + assertEquals(version.getMajor(), result.getVersion().getMajor()); + assertEquals(version.getMinor(), result.getVersion().getMinor()); + assertEquals(header.getPayloadLength(), result.getPayloadLength()); + assertEquals(containerId.getValue(), result.getContainerId().getValue()); + } + + @Test + void toModel_null() { + //When + Then + assertNull(ObjectHeaderMapper.toModel(null)); + assertNull(ObjectHeaderMapper.toModel(Types.Header.getDefaultInstance())); + } + + @Test + void toModel_notValidScheme() { + //Given + var header = Types.Header.newBuilder() + .setObjectType(Types.ObjectType.TOMBSTONE) + .build(); + + //When + Then + try (MockedStatic mockStatic = mockStatic(ObjectType.class)) { + mockStatic.when(() -> ObjectType.get(Types.ObjectType.TOMBSTONE.getNumber())) + .thenReturn(null); + + assertThrows(IllegalArgumentException.class, () -> ObjectHeaderMapper.toModel(header)); + } + } +} diff --git a/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectIdMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectIdMapperTest.java new file mode 100644 index 0000000..ab5a722 --- /dev/null +++ b/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectIdMapperTest.java @@ -0,0 +1,53 @@ +package info.frostfs.sdk.mappers.object; + +import com.google.protobuf.ByteString; +import frostfs.refs.Types; +import info.frostfs.sdk.dto.object.ObjectId; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class ObjectIdMapperTest { + + @Test + void toGrpcMessage_success() { + //Given + var objectId = new ObjectId("85orCLKSu3X1jGiTFmwmTUsBU88RBARNwuRwrEy5pyww"); + + //When + var result = ObjectIdMapper.toGrpcMessage(objectId); + + //Then + assertNotNull(result); + assertEquals(objectId.getValue(), new ObjectId(result.getValue().toByteArray()).getValue()); + } + + @Test + void toGrpcMessage_null() { + //When + Then + assertNull(ObjectIdMapper.toGrpcMessage(null)); + } + + @Test + void toModel_success() { + //Given + var objectId = new ObjectId("85orCLKSu3X1jGiTFmwmTUsBU88RBARNwuRwrEy5pyww"); + var objectIdGrpc = Types.ObjectID.newBuilder() + .setValue(ByteString.copyFrom(objectId.toHash())) + .build(); + + //When + var result = ObjectIdMapper.toModel(objectIdGrpc); + + //Then + assertNotNull(result); + assertEquals(objectId.getValue(), result.getValue()); + } + + @Test + void toModel_null() { + //When + Then + assertNull(ObjectIdMapper.toModel(null)); + assertNull(ObjectIdMapper.toModel(Types.ObjectID.getDefaultInstance())); + } +} From 0d76332f014082f788666c12b358ee6eedb0d323 Mon Sep 17 00:00:00 2001 From: Bruk Ori Date: Tue, 27 Aug 2024 18:30:41 +0300 Subject: [PATCH 13/38] [#6] fix naming Signed-off-by: Ori Bruk --- .../main/java/info/frostfs/sdk/mappers/SessionMapper.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/models/src/main/java/info/frostfs/sdk/mappers/SessionMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/SessionMapper.java index ee3deb7..ca14d05 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/SessionMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/SessionMapper.java @@ -9,14 +9,14 @@ import java.io.IOException; import static java.util.Objects.isNull; public class SessionMapper { - private static final String ERROR_TOKEN_MISSING_ERROR = "Token is not present"; + private static final String ERROR_TOKEN_MISSING_MESSAGE = "Token is not present"; private SessionMapper() { } public static byte[] serialize(Types.SessionToken token) { if (isNull(token)) { - throw new IllegalArgumentException(ERROR_TOKEN_MISSING_ERROR); + throw new IllegalArgumentException(ERROR_TOKEN_MISSING_MESSAGE); } try { @@ -31,7 +31,7 @@ public class SessionMapper { public static Types.SessionToken deserializeSessionToken(byte[] bytes) { if (isNull(bytes) || bytes.length == 0) { - throw new IllegalArgumentException(ERROR_TOKEN_MISSING_ERROR); + throw new IllegalArgumentException(ERROR_TOKEN_MISSING_MESSAGE); } try { From c9a54d56fbcc95f896bcd284724c968fd1a0ca08 Mon Sep 17 00:00:00 2001 From: Bruk Ori Date: Wed, 21 Aug 2024 10:57:47 +0300 Subject: [PATCH 14/38] [#6] cover the cryptography module with junit tests Signed-off-by: Ori Bruk --- .../java/info/frostfs/sdk/ArrayHelper.java | 8 + .../main/java/info/frostfs/sdk/Base58.java | 8 +- .../main/java/info/frostfs/sdk/Helper.java | 14 +- .../java/info/frostfs/sdk/KeyExtension.java | 55 ++++--- .../info/frostfs/sdk/ArrayHelperTest.java | 66 ++++++++ .../java/info/frostfs/sdk/Base58Test.java | 54 +++++++ .../java/info/frostfs/sdk/HelperTest.java | 110 +++++++++++++ .../info/frostfs/sdk/KeyExtensionTest.java | 151 ++++++++++++++++++ 8 files changed, 432 insertions(+), 34 deletions(-) create mode 100644 cryptography/src/test/java/info/frostfs/sdk/ArrayHelperTest.java create mode 100644 cryptography/src/test/java/info/frostfs/sdk/Base58Test.java create mode 100644 cryptography/src/test/java/info/frostfs/sdk/HelperTest.java create mode 100644 cryptography/src/test/java/info/frostfs/sdk/KeyExtensionTest.java diff --git a/cryptography/src/main/java/info/frostfs/sdk/ArrayHelper.java b/cryptography/src/main/java/info/frostfs/sdk/ArrayHelper.java index 915f202..b21a3f2 100644 --- a/cryptography/src/main/java/info/frostfs/sdk/ArrayHelper.java +++ b/cryptography/src/main/java/info/frostfs/sdk/ArrayHelper.java @@ -1,10 +1,18 @@ package info.frostfs.sdk; +import static java.util.Objects.isNull; + public class ArrayHelper { + private static final String ERROR_MESSAGE = "One of the input parameters is null"; + private ArrayHelper() { } public static byte[] concat(byte[] startArray, byte[] endArray) { + if (isNull(startArray) || isNull(endArray)) { + throw new IllegalArgumentException(ERROR_MESSAGE); + } + byte[] result = new byte[startArray.length + endArray.length]; System.arraycopy(startArray, 0, result, 0, startArray.length); diff --git a/cryptography/src/main/java/info/frostfs/sdk/Base58.java b/cryptography/src/main/java/info/frostfs/sdk/Base58.java index 860299d..4067687 100644 --- a/cryptography/src/main/java/info/frostfs/sdk/Base58.java +++ b/cryptography/src/main/java/info/frostfs/sdk/Base58.java @@ -16,6 +16,8 @@ public class Base58 { private static final char ENCODED_ZERO = ALPHABET[0]; private static final char BASE58_ASCII_MAX_VALUE = 128; private static final int[] INDEXES = new int[BASE58_ASCII_MAX_VALUE]; + private static final String ERROR_VALUE_MISSING_MESSAGE = "Input value is missing"; + private static final String ERROR_INVALID_CHARACTER_TEMPLATE = "Invalid character in Base58: 0x%04x"; static { Arrays.fill(INDEXES, -1); @@ -29,7 +31,7 @@ public class Base58 { public static byte[] base58CheckDecode(String input) { if (StringUtils.isEmpty(input)) { - throw new IllegalArgumentException("Input value is missing"); + throw new IllegalArgumentException(ERROR_VALUE_MISSING_MESSAGE); } byte[] buffer = decode(input); @@ -50,7 +52,7 @@ public class Base58 { public static String base58CheckEncode(byte[] data) { if (isNull(data)) { - throw new IllegalArgumentException("Input value is missing"); + throw new IllegalArgumentException(ERROR_VALUE_MISSING_MESSAGE); } byte[] checksum = getSha256(getSha256(data)); @@ -99,7 +101,7 @@ public class Base58 { char c = input.charAt(i); int digit = c < BASE58_ASCII_MAX_VALUE ? INDEXES[c] : -1; if (digit < 0) { - throw new IllegalArgumentException(String.format("Invalid character in Base58: 0x%04x", (int) c)); + throw new IllegalArgumentException(String.format(ERROR_INVALID_CHARACTER_TEMPLATE, (int) c)); } input58[i] = (byte) digit; } diff --git a/cryptography/src/main/java/info/frostfs/sdk/Helper.java b/cryptography/src/main/java/info/frostfs/sdk/Helper.java index a864301..529da35 100644 --- a/cryptography/src/main/java/info/frostfs/sdk/Helper.java +++ b/cryptography/src/main/java/info/frostfs/sdk/Helper.java @@ -11,6 +11,8 @@ import java.security.NoSuchAlgorithmException; import static java.util.Objects.isNull; public class Helper { + private static final String ERROR_MESSAGE = "Input value is missing"; + private static final String SHA256 = "SHA-256"; private static final int RIPEMD_160_HASH_BYTE_LENGTH = 20; private Helper() { @@ -18,7 +20,7 @@ public class Helper { public static byte[] getRipemd160(byte[] value) { if (isNull(value)) { - throw new IllegalArgumentException("Input value is missing"); + throw new IllegalArgumentException(ERROR_MESSAGE); } var hash = new byte[RIPEMD_160_HASH_BYTE_LENGTH]; @@ -30,7 +32,7 @@ public class Helper { public static MessageDigest getSha256Instance() { try { - return MessageDigest.getInstance("SHA-256"); + return MessageDigest.getInstance(SHA256); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } @@ -38,7 +40,7 @@ public class Helper { public static byte[] getSha256(byte[] value) { if (isNull(value)) { - throw new IllegalArgumentException("Input value is missing"); + throw new IllegalArgumentException(ERROR_MESSAGE); } return getSha256Instance().digest(value); @@ -46,15 +48,15 @@ public class Helper { public static ByteString getSha256(Message value) { if (isNull(value)) { - throw new IllegalArgumentException("Input value is missing"); + throw new IllegalArgumentException(ERROR_MESSAGE); } return ByteString.copyFrom(getSha256(value.toByteArray())); } public static String getHexString(byte[] value) { - if (isNull(value)) { - throw new IllegalArgumentException("Input value is missing"); + if (isNull(value) || value.length == 0) { + throw new IllegalArgumentException(ERROR_MESSAGE); } return String.format("%0" + (value.length << 1) + "x", new BigInteger(1, value)); diff --git a/cryptography/src/main/java/info/frostfs/sdk/KeyExtension.java b/cryptography/src/main/java/info/frostfs/sdk/KeyExtension.java index f3cf7de..25ce3af 100644 --- a/cryptography/src/main/java/info/frostfs/sdk/KeyExtension.java +++ b/cryptography/src/main/java/info/frostfs/sdk/KeyExtension.java @@ -31,6 +31,8 @@ import static org.bouncycastle.util.BigIntegers.fromUnsignedByteArray; public class KeyExtension { public static final byte NEO_ADDRESS_VERSION = 0x35; + private static final String CURVE_NAME = "secp256r1"; + private static final String SECURITY_ALGORITHM = "EC"; private static final int PS_IN_HASH160 = 0x0C; private static final int DECODE_ADDRESS_LENGTH = 21; private static final int COMPRESSED_PUBLIC_KEY_LENGTH = 33; @@ -38,6 +40,13 @@ public class KeyExtension { private static final int CHECK_SIG_DESCRIPTOR = ByteBuffer .wrap(getSha256("System.Crypto.CheckSig".getBytes(StandardCharsets.US_ASCII))) .order(ByteOrder.LITTLE_ENDIAN).getInt(); + private static final String ERROR_VALUE_MISSING_MESSAGE = "Input value is missing"; + private static final String ERROR_ENCODED_COMPRESSED_PUBLIC_KEY_TEMPLATE = + "PublicKey isn't encoded compressed public key. Expected length=%s, actual=%s"; + private static final String ERROR_UNCOMPRESSED_PUBLIC_KEY_TEMPLATE = + "Compress argument isn't uncompressed public key. Expected length=%s, actual=%s"; + private static final String ERROR_COMPRESSED_PUBLIC_KEY_TEMPLATE = + "Decompress argument isn't compressed public key. Expected length=%s, actual=%s"; private KeyExtension() { } @@ -45,10 +54,9 @@ public class KeyExtension { public static byte[] compress(byte[] publicKey) { checkInputValue(publicKey); if (publicKey.length != UNCOMPRESSED_PUBLIC_KEY_LENGTH) { - throw new IllegalArgumentException( - String.format("Compress argument isn't uncompressed public key. Expected length=%s, actual=%s", - UNCOMPRESSED_PUBLIC_KEY_LENGTH, publicKey.length) - ); + throw new IllegalArgumentException(String.format( + ERROR_UNCOMPRESSED_PUBLIC_KEY_TEMPLATE, UNCOMPRESSED_PUBLIC_KEY_LENGTH, publicKey.length + )); } var secp256R1 = SECNamedCurves.getByOID(SECObjectIdentifiers.secp256r1); @@ -58,7 +66,7 @@ public class KeyExtension { public static byte[] getPrivateKeyFromWIF(String wif) { if (StringUtils.isEmpty(wif)) { - throw new IllegalArgumentException("Input value is missing"); + throw new IllegalArgumentException(ERROR_VALUE_MISSING_MESSAGE); } var data = Base58.base58CheckDecode(wif); @@ -87,11 +95,11 @@ public class KeyExtension { ECPrivateKeyParameters ecParams = new ECPrivateKeyParameters(fromUnsignedByteArray(privateKey), domain); ECParameterSpec ecParameterSpec = new ECNamedCurveSpec( - "secp256r1", params.getCurve(), params.getG(), params.getN(), params.getH() + CURVE_NAME, params.getCurve(), params.getG(), params.getN(), params.getH() ); ECPrivateKeySpec privateKeySpec = new ECPrivateKeySpec(ecParams.getD(), ecParameterSpec); try { - KeyFactory kf = KeyFactory.getInstance("EC"); + KeyFactory kf = KeyFactory.getInstance(SECURITY_ALGORITHM); return kf.generatePrivate(privateKeySpec); } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { throw new RuntimeException(e); @@ -103,8 +111,7 @@ public class KeyExtension { if (publicKey.length != COMPRESSED_PUBLIC_KEY_LENGTH) { throw new IllegalArgumentException( - String.format("Decompress argument isn't compressed public key. Expected length=%s, actual=%s", - COMPRESSED_PUBLIC_KEY_LENGTH, publicKey.length) + String.format(ERROR_COMPRESSED_PUBLIC_KEY_TEMPLATE, COMPRESSED_PUBLIC_KEY_LENGTH, publicKey.length) ); } @@ -118,13 +125,13 @@ public class KeyExtension { publicParams.getQ().getRawXCoord().toBigInteger(), publicParams.getQ().getRawYCoord().toBigInteger() ); ECParameterSpec ecParameterSpec = new ECNamedCurveSpec( - "secp256r1", secp256R1.getCurve(), secp256R1.getG(), secp256R1.getN(), - secp256R1.getH(), secp256R1.getSeed() + CURVE_NAME, secp256R1.getCurve(), secp256R1.getG(), secp256R1.getN(), secp256R1.getH(), + secp256R1.getSeed() ); ECPublicKeySpec publicKeySpec = new ECPublicKeySpec(point, ecParameterSpec); try { - KeyFactory kf = KeyFactory.getInstance("EC"); + KeyFactory kf = KeyFactory.getInstance(SECURITY_ALGORITHM); return kf.generatePublic(publicKeySpec); } catch (Exception e) { throw new RuntimeException(e); @@ -141,19 +148,18 @@ public class KeyExtension { public static String publicKeyToAddress(byte[] publicKey) { checkInputValue(publicKey); if (publicKey.length != COMPRESSED_PUBLIC_KEY_LENGTH) { - throw new IllegalArgumentException( - String.format("PublicKey isn't encoded compressed public key. Expected length=%s, actual=%s", - COMPRESSED_PUBLIC_KEY_LENGTH, publicKey.length) - ); + throw new IllegalArgumentException(String.format( + ERROR_ENCODED_COMPRESSED_PUBLIC_KEY_TEMPLATE, COMPRESSED_PUBLIC_KEY_LENGTH, publicKey.length + )); } - return toAddress(getScriptHash(publicKey), NEO_ADDRESS_VERSION); + return toAddress(getScriptHash(publicKey)); } - private static String toAddress(byte[] scriptHash, byte version) { + private static String toAddress(byte[] scriptHash) { checkInputValue(scriptHash); byte[] data = new byte[DECODE_ADDRESS_LENGTH]; - data[0] = version; + data[0] = NEO_ADDRESS_VERSION; System.arraycopy(scriptHash, 0, data, 1, scriptHash.length); return Base58.base58CheckEncode(data); } @@ -171,10 +177,9 @@ public class KeyExtension { private static byte[] createSignatureRedeemScript(byte[] publicKey) { checkInputValue(publicKey); if (publicKey.length != COMPRESSED_PUBLIC_KEY_LENGTH) { - throw new IllegalArgumentException( - String.format("PublicKey isn't encoded compressed public key. Expected length=%s, actual=%s", - COMPRESSED_PUBLIC_KEY_LENGTH, publicKey.length) - ); + throw new IllegalArgumentException(String.format( + ERROR_ENCODED_COMPRESSED_PUBLIC_KEY_TEMPLATE, COMPRESSED_PUBLIC_KEY_LENGTH, publicKey.length + )); } var script = new byte[]{PS_IN_HASH160, COMPRESSED_PUBLIC_KEY_LENGTH}; //PUSHDATA1 33 @@ -186,8 +191,8 @@ public class KeyExtension { } private static void checkInputValue(byte[] data) { - if (isNull(data)) { - throw new IllegalArgumentException("Input value is missing"); + if (isNull(data) || data.length == 0) { + throw new IllegalArgumentException(ERROR_VALUE_MISSING_MESSAGE); } } } diff --git a/cryptography/src/test/java/info/frostfs/sdk/ArrayHelperTest.java b/cryptography/src/test/java/info/frostfs/sdk/ArrayHelperTest.java new file mode 100644 index 0000000..ab8a1e1 --- /dev/null +++ b/cryptography/src/test/java/info/frostfs/sdk/ArrayHelperTest.java @@ -0,0 +1,66 @@ +package info.frostfs.sdk; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class ArrayHelperTest { + + @Test + void concatTest_success() { + //Given + var startBytes = new byte[]{1, 2, 3, 4, 5}; + var endBytes = new byte[]{6, 7, 8, 9}; + + //When + var result = ArrayHelper.concat(startBytes, endBytes); + + //Then + assertThat(startBytes).hasSize(5); + assertThat(endBytes).hasSize(4); + assertThat(result).hasSize(9).startsWith(startBytes).endsWith(endBytes); + } + + @Test + void concatTest_endArrayIsEmpty() { + //Given + var startBytes = new byte[]{1, 2, 3, 4, 5}; + var endBytes = new byte[]{}; + + //When + var result = ArrayHelper.concat(startBytes, endBytes); + + //Then + assertThat(startBytes).hasSize(5); + assertThat(endBytes).hasSize(0); + assertThat(result).hasSize(5).startsWith(startBytes); + } + + @Test + void concatTest_startArrayIsEmpty() { + //Given + var startBytes = new byte[]{}; + var endBytes = new byte[]{6, 7, 8, 9}; + + //When + var result = ArrayHelper.concat(startBytes, endBytes); + + //Then + assertThat(startBytes).hasSize(0); + assertThat(endBytes).hasSize(4); + assertThat(result).hasSize(4).startsWith(endBytes); + } + + @Test + void concatTest_givenParamsIsNull() { + //Given + var startBytes = new byte[]{1, 2, 3, 4, 5}; + var endBytes = new byte[]{6, 7, 8, 9}; + + //When + Then + assertThrows(IllegalArgumentException.class, () -> ArrayHelper.concat(startBytes, null)); + assertThrows(IllegalArgumentException.class, () -> ArrayHelper.concat(null, endBytes)); + assertThrows(IllegalArgumentException.class, () -> ArrayHelper.concat(null, null)); + } +} diff --git a/cryptography/src/test/java/info/frostfs/sdk/Base58Test.java b/cryptography/src/test/java/info/frostfs/sdk/Base58Test.java new file mode 100644 index 0000000..7ac3d9d --- /dev/null +++ b/cryptography/src/test/java/info/frostfs/sdk/Base58Test.java @@ -0,0 +1,54 @@ +package info.frostfs.sdk; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class Base58Test { + private static final String WIF = "L1YS4myg3xHPvi3FHeLaEt7G8upwJaWL5YLV7huviuUtXFpzBMqZ"; + private static final byte[] DECODE = new byte[]{ + -128, -128, -5, 30, -36, -118, 85, -67, -6, 81, 43, 93, -38, 106, 21, -88, 127, 15, 125, -79, -17, -40, 77, + -15, 122, -88, 72, 109, -47, 125, -80, -40, -38, 1 + }; + + @Test + void base58DecodeEncode() { + //When + Then + assertEquals(WIF, Base58.base58CheckEncode(Base58.base58CheckDecode(WIF))); + } + + @Test + void base58Decode_success() { + //When + var decode = Base58.base58CheckDecode(WIF); + + //Then + assertThat(decode).hasSize(34).containsExactly(DECODE); + } + + @Test + void base58Decode_wrong() { + //When + Then + assertThrows(IllegalArgumentException.class, () -> Base58.base58CheckDecode(null)); + assertThrows(IllegalArgumentException.class, () -> Base58.base58CheckDecode("")); + assertThrows(IllegalArgumentException.class, () -> Base58.base58CheckDecode("WIF")); + assertThrows(IllegalArgumentException.class, () -> Base58.base58CheckDecode("fh")); + } + + @Test + void base58Encode_success() { + //When + var encode = Base58.base58CheckEncode(DECODE); + + //Then + assertEquals(WIF, encode); + } + + @Test + void base58Encode_wrong() { + //When + Then + assertThrows(IllegalArgumentException.class, () -> Base58.base58CheckEncode(null)); + } +} diff --git a/cryptography/src/test/java/info/frostfs/sdk/HelperTest.java b/cryptography/src/test/java/info/frostfs/sdk/HelperTest.java new file mode 100644 index 0000000..67cfaba --- /dev/null +++ b/cryptography/src/test/java/info/frostfs/sdk/HelperTest.java @@ -0,0 +1,110 @@ +package info.frostfs.sdk; + +import com.google.protobuf.Message; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class HelperTest { + + @Test + void getRipemd160_success() { + //Given + var value = new byte[]{1, 2, 3, 4, 5}; + var expected = new byte[] + {-21, -126, 92, 75, 36, -12, 37, 7, 122, 6, 124, -61, -66, -12, 87, 120, 63, 90, -41, 5}; + //When + var result = Helper.getRipemd160(value); + + //Then + assertThat(result).hasSize(20).containsExactly(expected); + } + + @Test + void getRipemd160_givenParamIsNull() { + //When + Then + assertThrows(IllegalArgumentException.class, () -> Helper.getRipemd160(null)); + } + + @Test + void getRipemd160_givenParamsIsEmpty() { + //Given + var value = new byte[]{}; + var expected = new byte[] + {-100, 17, -123, -91, -59, -23, -4, 84, 97, 40, 8, -105, 126, -24, -11, 72, -78, 37, -115, 49}; + //When + var result = Helper.getRipemd160(value); + + //Then + assertThat(result).hasSize(20).containsExactly(expected); + } + + @Test + void getSha256Instance() { + //When + var result = Helper.getSha256Instance(); + + //Then + assertEquals("SHA-256", result.getAlgorithm()); + } + + @Test + void getSha256_bytes_success() { + //Given + var value = new byte[]{1, 2, 3, 4, 5}; + var expected = new byte[]{ + 116, -8, 31, -31, 103, -39, -101, 76, -76, 29, 109, 12, -51, -88, 34, 120, -54, -18, -97, 62, 47, 37, + -43, -27, -93, -109, 111, -13, -36, -20, 96, -48 + }; + + //When + var result = Helper.getSha256(value); + + //Then + assertThat(result).hasSize(32).containsExactly(expected); + } + + @Test + void getSha256_bytes_givenParamIsNull() { + //When + Then + assertThrows(IllegalArgumentException.class, () -> Helper.getSha256((byte[]) null)); + assertThrows(IllegalArgumentException.class, () -> Helper.getSha256((Message) null)); + } + + @Test + void getSha256_bytes_givenParamsIsEmpty() { + //Given + var value = new byte[]{}; + var expected = new byte[]{ + -29, -80, -60, 66, -104, -4, 28, 20, -102, -5, -12, -56, -103, 111, -71, 36, 39, -82, 65, -28, 100, + -101, -109, 76, -92, -107, -103, 27, 120, 82, -72, 85 + }; + + //When + var result = Helper.getSha256(value); + + //Then + assertThat(result).hasSize(32).containsExactly(expected); + } + + @Test + void getHexString_success() { + //Given + var value = new byte[]{1, 2, 3, 4, 5}; + + //When + var result = Helper.getHexString(value); + + //Then + assertEquals("0102030405", result); + } + + @Test + void getHexString_wrong() { + //When + Then + assertThrows(IllegalArgumentException.class, () -> Helper.getHexString(null)); + assertThrows(IllegalArgumentException.class, () -> Helper.getHexString(new byte[]{})); + } +} diff --git a/cryptography/src/test/java/info/frostfs/sdk/KeyExtensionTest.java b/cryptography/src/test/java/info/frostfs/sdk/KeyExtensionTest.java new file mode 100644 index 0000000..c54025a --- /dev/null +++ b/cryptography/src/test/java/info/frostfs/sdk/KeyExtensionTest.java @@ -0,0 +1,151 @@ +package info.frostfs.sdk; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class KeyExtensionTest { + private static final String WIF = "L1YS4myg3xHPvi3FHeLaEt7G8upwJaWL5YLV7huviuUtXFpzBMqZ"; + private static final String OWNER_ID = "NVxUSpEEJzYXZZtUs18PrJTD9QZkLLNQ8S"; + private static final byte[] PRIVATE_KEY = new byte[]{ + -128, -5, 30, -36, -118, 85, -67, -6, 81, 43, 93, -38, 106, 21, -88, 127, 15, 125, -79, -17, -40, 77, -15, + 122, -88, 72, 109, -47, 125, -80, -40, -38 + }; + private static final byte[] PUBLIC_KEY = new byte[]{ + 3, 80, -111, 65, 23, 36, -4, -69, 80, 102, 98, -13, 13, -79, 24, 126, -116, 90, 56, 44, 127, 106, -125, 99, + -48, -94, 104, 81, 120, -110, 30, 80, 70 + }; + private static final byte[] UNCOMPRESSED_PUBLIC_KEY = new byte[]{ + 4, 80, -111, 65, 23, 36, -4, -69, 80, 102, 98, -13, 13, -79, 24, 126, -116, 90, 56, 44, 127, 106, -125, 99, + -48, -94, 104, 81, 120, -110, 30, 80, 70, 76, -18, 53, -9, 79, -8, -25, -69, 12, 89, -103, 15, 126, 118, + -68, -73, 65, -57, -26, 75, 4, -51, -40, -20, 75, 89, -59, 111, 96, -80, 56, 13 + }; + + @Test + void getPrivateKeyFromWIF_success() { + //When + var privateKey = KeyExtension.getPrivateKeyFromWIF(WIF); + + //Then + assertThat(privateKey).hasSize(32).containsExactly(PRIVATE_KEY); + } + + @Test + void getPrivateKeyFromWIF_wrong() { + //When + Then + assertThrows(IllegalArgumentException.class, () -> KeyExtension.getPrivateKeyFromWIF("")); + assertThrows(IllegalArgumentException.class, () -> KeyExtension.getPrivateKeyFromWIF(null)); + } + + @Test + void loadPublicKey_success() { + //When + var publicKey = KeyExtension.loadPublicKey(PRIVATE_KEY); + + //Then + assertThat(publicKey).hasSize(33).containsExactly(PUBLIC_KEY); + } + + @Test + void loadPublicKey_wrong() { + //When + Then + assertThrows(IllegalArgumentException.class, () -> KeyExtension.loadPublicKey(null)); + assertThrows(IllegalArgumentException.class, () -> KeyExtension.loadPublicKey(new byte[]{})); + } + + @Test + void loadPrivateKey_success() { + //When + var privateKey = KeyExtension.loadPrivateKey(PRIVATE_KEY); + + //Then + assertThat(privateKey.getEncoded()).hasSize(67).endsWith(PRIVATE_KEY); + assertEquals("EC", privateKey.getAlgorithm()); + assertEquals("PKCS#8", privateKey.getFormat()); + } + + @Test + void loadPrivateKey_wrong() { + //When + Then + assertThrows(IllegalArgumentException.class, () -> KeyExtension.loadPrivateKey(null)); + assertThrows(IllegalArgumentException.class, () -> KeyExtension.loadPrivateKey(new byte[]{})); + } + + @Test + void getPublicKeyFromBytes_success() { + //When + var publicKey = KeyExtension.getPublicKeyFromBytes(PUBLIC_KEY); + + //Then + assertThat(publicKey.getEncoded()).hasSize(91).endsWith(UNCOMPRESSED_PUBLIC_KEY); + assertEquals("EC", publicKey.getAlgorithm()); + assertEquals("X.509", publicKey.getFormat()); + } + + @Test + void getPublicKeyFromBytes_wrong() { + //When + Then + assertThrows(IllegalArgumentException.class, () -> KeyExtension.getPublicKeyFromBytes(null)); + assertThrows(IllegalArgumentException.class, () -> KeyExtension.getPublicKeyFromBytes(new byte[]{})); + assertThrows(IllegalArgumentException.class, () -> KeyExtension.getPublicKeyFromBytes(PRIVATE_KEY)); + } + + @Test + void getScriptHash_success() { + //Given + var expected = new byte[]{ + 110, 42, -125, -76, -25, -44, -94, 22, -98, 117, -100, -5, 103, 74, -128, -51, 37, -116, -102, 71 + }; + + //When + var hash = KeyExtension.getScriptHash(PUBLIC_KEY); + + //Then + assertThat(hash).hasSize(20).containsExactly(expected); + } + + @Test + void getScriptHash_wrong() { + //When + Then + assertThrows(IllegalArgumentException.class, () -> KeyExtension.getScriptHash(null)); + assertThrows(IllegalArgumentException.class, () -> KeyExtension.getScriptHash(new byte[]{})); + assertThrows(IllegalArgumentException.class, () -> KeyExtension.getScriptHash(PRIVATE_KEY)); + } + + @Test + void publicKeyToAddress_success() { + //When + var address = KeyExtension.publicKeyToAddress(PUBLIC_KEY); + + //Then + assertEquals(OWNER_ID, address); + } + + @Test + void publicKeyToAddress_wrong() { + //When + Then + assertThrows(IllegalArgumentException.class, () -> KeyExtension.publicKeyToAddress(null)); + assertThrows(IllegalArgumentException.class, () -> KeyExtension.publicKeyToAddress(new byte[]{})); + assertThrows(IllegalArgumentException.class, () -> KeyExtension.publicKeyToAddress(PRIVATE_KEY)); + } + + @Test + void compress_success() { + //When + var publicKey = KeyExtension.compress(UNCOMPRESSED_PUBLIC_KEY); + + //Then + assertThat(publicKey).hasSize(33).containsExactly(PUBLIC_KEY); + } + + @Test + void compress_wrong() { + //When + Then + assertThrows(IllegalArgumentException.class, () -> KeyExtension.compress(null)); + assertThrows(IllegalArgumentException.class, () -> KeyExtension.compress(new byte[]{})); + assertThrows(IllegalArgumentException.class, () -> KeyExtension.compress(PUBLIC_KEY)); + } + +} From 23bbe08893d6cb08a3a4d4ef4257d0bfb8a43b4d Mon Sep 17 00:00:00 2001 From: Bruk Ori Date: Wed, 11 Sep 2024 11:41:38 +0300 Subject: [PATCH 15/38] [#12] Extend method logic Signed-off-by: Ori Bruk --- .../java/info/frostfs/sdk/FrostFSClient.java | 17 +- .../info/frostfs/sdk/enums/WaitExpects.java | 6 + .../sdk/exceptions/ResponseException.java | 16 + .../sdk/exceptions/TimeoutException.java | 6 + .../frostfs/sdk/jdo/ClientEnvironment.java | 8 +- .../info/frostfs/sdk/jdo/ClientSettings.java | 48 +- .../main/java/info/frostfs/sdk/jdo/ECDsa.java | 4 +- .../info/frostfs/sdk/jdo/NetworkSettings.java | 34 +- .../frostfs/sdk/jdo/PutObjectParameters.java | 60 ++- .../info/frostfs/sdk/jdo/PutObjectResult.java | 21 + .../info/frostfs/sdk/jdo/WaitParameters.java | 34 ++ .../frostfs/sdk/services/ObjectClient.java | 4 +- .../frostfs/sdk/services/SessionClient.java | 2 +- .../frostfs/sdk/services/SessionTools.java | 9 + .../services/impl/ContainerClientImpl.java | 197 +++++-- .../sdk/services/impl/NetmapClientImpl.java | 17 +- .../sdk/services/impl/ObjectClientImpl.java | 485 +++++++++++------- .../sdk/services/impl/ObjectToolsImpl.java | 97 ++-- .../sdk/services/impl/SessionClientImpl.java | 23 +- .../sdk/services/impl/SessionToolsImpl.java | 27 + ...bjectReader.java => ObjectReaderImpl.java} | 5 +- .../services/impl/rwhelper/ObjectWriter.java | 4 +- .../info/frostfs/sdk/tools/GrpcClient.java | 15 +- .../info/frostfs/sdk/tools/MessageHelper.java | 18 - .../frostfs/sdk/tools/RequestConstructor.java | 117 ++++- .../info/frostfs/sdk/tools/RequestSigner.java | 9 +- .../java/info/frostfs/sdk/tools/Verifier.java | 10 +- .../info/frostfs/sdk/utils/MessageHelper.java | 30 ++ .../frostfs/sdk/utils/ValidatorUtils.java | 42 ++ .../java/info/frostfs/sdk/utils/WaitUtil.java | 12 + .../java/info/frostfs/sdk/ArrayHelper.java | 8 +- .../frostfs/sdk/constants/FilterConst.java | 49 ++ .../java/info/frostfs/sdk/dto/CheckSum.java | 25 + .../info/frostfs/sdk/dto/SessionToken.java | 19 - .../frostfs/sdk/dto/container/Container.java | 2 +- .../sdk/dto/netmap/NetmapSnapshot.java | 3 +- .../info/frostfs/sdk/dto/netmap/NodeInfo.java | 6 +- .../frostfs/sdk/dto/{ => netmap}/Version.java | 2 +- .../frostfs/sdk/dto/object/LinkObject.java | 6 +- .../frostfs/sdk/dto/object/ObjectFilter.java | 118 ++++- .../frostfs/sdk/dto/object/ObjectFrostFS.java | 15 +- .../frostfs/sdk/dto/object/ObjectHeader.java | 32 +- .../frostfs/sdk/dto/object/ObjectReader.java | 5 + .../frostfs/sdk/dto/{ => object}/OwnerId.java | 7 +- .../frostfs/sdk/dto/{ => object}/Split.java | 15 +- .../frostfs/sdk/dto/{ => object}/SplitId.java | 6 +- .../sdk/dto/{ => response}/MetaHeader.java | 4 +- .../ResponseStatus.java} | 8 +- .../sdk/dto/{ => response}/Signature.java | 2 +- .../frostfs/sdk/dto/session/SessionToken.java | 13 + .../frostfs/sdk/mappers/MetaHeaderMapper.java | 24 - .../mappers/container/ContainerMapper.java | 2 +- .../sdk/mappers/netmap/NodeInfoMapper.java | 1 - .../mappers/{ => netmap}/VersionMapper.java | 4 +- .../mappers/object/ObjectFilterMapper.java | 4 +- .../mappers/object/ObjectHeaderMapper.java | 19 +- .../mappers/{ => object}/OwnerIdMapper.java | 4 +- .../mappers/response/MetaHeaderMapper.java | 33 ++ .../ResponseStatusMapper.java} | 14 +- .../{ => response}/SignatureMapper.java | 4 +- .../mappers/{ => session}/SessionMapper.java | 2 +- .../sdk/mappers/MetaHeaderMapperTest.java | 33 -- .../{ => netmap}/VersionMapperTest.java | 4 +- .../object/ObjectFilterMapperTest.java | 4 +- .../object/ObjectHeaderMapperTest.java | 11 +- .../{ => object}/OwnerIdMapperTest.java | 4 +- .../response/MetaHeaderMapperTest.java | 57 ++ .../ResponseStatusMapperTest.java} | 10 +- .../{ => response}/SignatureMapperTest.java | 4 +- .../{ => session}/SessionMapperTest.java | 2 +- 70 files changed, 1375 insertions(+), 587 deletions(-) create mode 100644 client/src/main/java/info/frostfs/sdk/enums/WaitExpects.java create mode 100644 client/src/main/java/info/frostfs/sdk/exceptions/ResponseException.java create mode 100644 client/src/main/java/info/frostfs/sdk/exceptions/TimeoutException.java create mode 100644 client/src/main/java/info/frostfs/sdk/jdo/PutObjectResult.java create mode 100644 client/src/main/java/info/frostfs/sdk/jdo/WaitParameters.java create mode 100644 client/src/main/java/info/frostfs/sdk/services/SessionTools.java create mode 100644 client/src/main/java/info/frostfs/sdk/services/impl/SessionToolsImpl.java rename client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/{ObjectReader.java => ObjectReaderImpl.java} (89%) delete mode 100644 client/src/main/java/info/frostfs/sdk/tools/MessageHelper.java create mode 100644 client/src/main/java/info/frostfs/sdk/utils/MessageHelper.java create mode 100644 client/src/main/java/info/frostfs/sdk/utils/ValidatorUtils.java create mode 100644 client/src/main/java/info/frostfs/sdk/utils/WaitUtil.java create mode 100644 models/src/main/java/info/frostfs/sdk/constants/FilterConst.java create mode 100644 models/src/main/java/info/frostfs/sdk/dto/CheckSum.java delete mode 100644 models/src/main/java/info/frostfs/sdk/dto/SessionToken.java rename models/src/main/java/info/frostfs/sdk/dto/{ => netmap}/Version.java (95%) create mode 100644 models/src/main/java/info/frostfs/sdk/dto/object/ObjectReader.java rename models/src/main/java/info/frostfs/sdk/dto/{ => object}/OwnerId.java (85%) rename models/src/main/java/info/frostfs/sdk/dto/{ => object}/Split.java (78%) rename models/src/main/java/info/frostfs/sdk/dto/{ => object}/SplitId.java (83%) rename models/src/main/java/info/frostfs/sdk/dto/{ => response}/MetaHeader.java (94%) rename models/src/main/java/info/frostfs/sdk/dto/{Status.java => response/ResponseStatus.java} (83%) rename models/src/main/java/info/frostfs/sdk/dto/{ => response}/Signature.java (93%) create mode 100644 models/src/main/java/info/frostfs/sdk/dto/session/SessionToken.java delete mode 100644 models/src/main/java/info/frostfs/sdk/mappers/MetaHeaderMapper.java rename models/src/main/java/info/frostfs/sdk/mappers/{ => netmap}/VersionMapper.java (88%) rename models/src/main/java/info/frostfs/sdk/mappers/{ => object}/OwnerIdMapper.java (84%) create mode 100644 models/src/main/java/info/frostfs/sdk/mappers/response/MetaHeaderMapper.java rename models/src/main/java/info/frostfs/sdk/mappers/{StatusMapper.java => response/ResponseStatusMapper.java} (58%) rename models/src/main/java/info/frostfs/sdk/mappers/{ => response}/SignatureMapper.java (91%) rename models/src/main/java/info/frostfs/sdk/mappers/{ => session}/SessionMapper.java (96%) delete mode 100644 models/src/test/java/info/frostfs/sdk/mappers/MetaHeaderMapperTest.java rename models/src/test/java/info/frostfs/sdk/mappers/{ => netmap}/VersionMapperTest.java (93%) rename models/src/test/java/info/frostfs/sdk/mappers/{ => object}/OwnerIdMapperTest.java (92%) create mode 100644 models/src/test/java/info/frostfs/sdk/mappers/response/MetaHeaderMapperTest.java rename models/src/test/java/info/frostfs/sdk/mappers/{StatusMapperTest.java => response/ResponseStatusMapperTest.java} (80%) rename models/src/test/java/info/frostfs/sdk/mappers/{ => response}/SignatureMapperTest.java (95%) rename models/src/test/java/info/frostfs/sdk/mappers/{ => session}/SessionMapperTest.java (97%) diff --git a/client/src/main/java/info/frostfs/sdk/FrostFSClient.java b/client/src/main/java/info/frostfs/sdk/FrostFSClient.java index 48fdcc5..a373559 100644 --- a/client/src/main/java/info/frostfs/sdk/FrostFSClient.java +++ b/client/src/main/java/info/frostfs/sdk/FrostFSClient.java @@ -1,16 +1,16 @@ package info.frostfs.sdk; import frostfs.session.Types; -import info.frostfs.sdk.dto.SessionToken; -import info.frostfs.sdk.dto.Version; import info.frostfs.sdk.dto.container.Container; import info.frostfs.sdk.dto.container.ContainerId; import info.frostfs.sdk.dto.netmap.NetmapSnapshot; import info.frostfs.sdk.dto.netmap.NodeInfo; +import info.frostfs.sdk.dto.netmap.Version; import info.frostfs.sdk.dto.object.ObjectFilter; import info.frostfs.sdk.dto.object.ObjectFrostFS; import info.frostfs.sdk.dto.object.ObjectHeader; import info.frostfs.sdk.dto.object.ObjectId; +import info.frostfs.sdk.dto.session.SessionToken; import info.frostfs.sdk.jdo.ClientEnvironment; import info.frostfs.sdk.jdo.ClientSettings; import info.frostfs.sdk.jdo.NetworkSettings; @@ -23,6 +23,7 @@ import java.util.List; import static info.frostfs.sdk.tools.GrpcClient.initGrpcChannel; import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; public class FrostFSClient implements ContainerClient, ObjectClient, NetmapClient, SessionClient, ToolsClient { private static final String ERROR_CLIENT_OPTIONS_INIT = "Options must be initialized."; @@ -39,9 +40,8 @@ public class FrostFSClient implements ContainerClient, ObjectClient, NetmapClien throw new IllegalArgumentException(ERROR_CLIENT_OPTIONS_INIT); } - clientSettings.validate(); - - Channel channel = initGrpcChannel(clientSettings.getHost(), clientSettings.getCreds()); + Channel channel = nonNull(clientSettings.getChannel()) ? clientSettings.getChannel() + : initGrpcChannel(clientSettings.getHost(), clientSettings.getCredentials()); ClientEnvironment clientEnvironment = new ClientEnvironment(clientSettings.getKey(), channel, new Version(), this); @@ -98,13 +98,18 @@ public class FrostFSClient implements ContainerClient, ObjectClient, NetmapClien return objectClientImpl.putObject(parameters); } + @Override + public ObjectId putSingleObject(ObjectFrostFS objectFrostFS) { + return objectClientImpl.putSingleObject(objectFrostFS); + } + @Override public void deleteObject(ContainerId containerId, ObjectId objectId) { objectClientImpl.deleteObject(containerId, objectId); } @Override - public Iterable searchObjects(ContainerId cid, ObjectFilter... filters) { + public Iterable searchObjects(ContainerId cid, ObjectFilter... filters) { return objectClientImpl.searchObjects(cid, filters); } diff --git a/client/src/main/java/info/frostfs/sdk/enums/WaitExpects.java b/client/src/main/java/info/frostfs/sdk/enums/WaitExpects.java new file mode 100644 index 0000000..82e4d0a --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/enums/WaitExpects.java @@ -0,0 +1,6 @@ +package info.frostfs.sdk.enums; + +public enum WaitExpects { + EXISTS, + REMOVED +} diff --git a/client/src/main/java/info/frostfs/sdk/exceptions/ResponseException.java b/client/src/main/java/info/frostfs/sdk/exceptions/ResponseException.java new file mode 100644 index 0000000..1367bd1 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/exceptions/ResponseException.java @@ -0,0 +1,16 @@ +package info.frostfs.sdk.exceptions; + +import info.frostfs.sdk.dto.response.ResponseStatus; + +public class ResponseException extends RuntimeException { + private final ResponseStatus status; + + public ResponseException(ResponseStatus status) { + super(status.toString()); + this.status = status; + } + + public ResponseStatus getStatus() { + return status; + } +} diff --git a/client/src/main/java/info/frostfs/sdk/exceptions/TimeoutException.java b/client/src/main/java/info/frostfs/sdk/exceptions/TimeoutException.java new file mode 100644 index 0000000..b619ddd --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/exceptions/TimeoutException.java @@ -0,0 +1,6 @@ +package info.frostfs.sdk.exceptions; + +public class TimeoutException extends RuntimeException { + public TimeoutException() { + } +} diff --git a/client/src/main/java/info/frostfs/sdk/jdo/ClientEnvironment.java b/client/src/main/java/info/frostfs/sdk/jdo/ClientEnvironment.java index 6e17947..22bc8cd 100644 --- a/client/src/main/java/info/frostfs/sdk/jdo/ClientEnvironment.java +++ b/client/src/main/java/info/frostfs/sdk/jdo/ClientEnvironment.java @@ -1,14 +1,16 @@ package info.frostfs.sdk.jdo; import info.frostfs.sdk.FrostFSClient; -import info.frostfs.sdk.dto.OwnerId; -import info.frostfs.sdk.dto.Version; +import info.frostfs.sdk.dto.netmap.Version; +import info.frostfs.sdk.dto.object.OwnerId; import io.grpc.Channel; import org.apache.commons.lang3.StringUtils; import static java.util.Objects.isNull; public class ClientEnvironment { + private static final String ERROR_MESSAGE = "One of the input attributes is missing"; + private final OwnerId ownerId; private final Version version; private final ECDsa key; @@ -18,7 +20,7 @@ public class ClientEnvironment { public ClientEnvironment(String wif, Channel channel, Version version, FrostFSClient frostFSClient) { if (StringUtils.isEmpty(wif) || isNull(channel) || isNull(version) || isNull(frostFSClient)) { - throw new IllegalArgumentException("One of the input attributes is missing"); + throw new IllegalArgumentException(ERROR_MESSAGE); } this.key = new ECDsa(wif); diff --git a/client/src/main/java/info/frostfs/sdk/jdo/ClientSettings.java b/client/src/main/java/info/frostfs/sdk/jdo/ClientSettings.java index 5f7bd32..a4962a0 100644 --- a/client/src/main/java/info/frostfs/sdk/jdo/ClientSettings.java +++ b/client/src/main/java/info/frostfs/sdk/jdo/ClientSettings.java @@ -1,14 +1,20 @@ package info.frostfs.sdk.jdo; +import io.grpc.Channel; import io.grpc.ChannelCredentials; import org.apache.commons.lang3.StringUtils; -public class ClientSettings { - private static final String ERROR_TEMPLATE = "%s is required parameter."; +import static java.util.Objects.isNull; - public String key; - public String host; - public ChannelCredentials creds; +public class ClientSettings { + private static final String ERROR_TEMPLATE = "%s required parameter."; + private static final String KEY_NAME = "Key"; + private static final String HOST_AND_CHANNEL_NAME = "Host or Channel"; + + private final String key; + private String host; + private ChannelCredentials credentials; + private Channel channel; public ClientSettings(String key, String host) { this.key = key; @@ -16,46 +22,44 @@ public class ClientSettings { validate(); } - public ClientSettings(String key, String host, ChannelCredentials creds) { + public ClientSettings(String key, String host, ChannelCredentials credentials) { this.key = key; this.host = host; - this.creds = creds; + this.credentials = credentials; validate(); } - public ChannelCredentials getCreds() { - return creds; + public ClientSettings(String key, Channel channel) { + this.key = key; + this.channel = channel; + validate(); } - public void setCreds(ChannelCredentials creds) { - this.creds = creds; + public Channel getChannel() { + return channel; + } + + public ChannelCredentials getCredentials() { + return credentials; } public String getHost() { return host; } - public void setHost(String host) { - this.host = host; - } - public String getKey() { return key; } - public void setKey(String key) { - this.key = key; - } - public void validate() { StringBuilder errorMessage = new StringBuilder(); if (StringUtils.isEmpty(key)) { - errorMessage.append(String.format(ERROR_TEMPLATE, "Key")).append(System.lineSeparator()); + errorMessage.append(String.format(ERROR_TEMPLATE, KEY_NAME)).append(System.lineSeparator()); } - if (StringUtils.isEmpty(host)) { - errorMessage.append(String.format(ERROR_TEMPLATE, "Host")).append(System.lineSeparator()); + if (StringUtils.isEmpty(host) && isNull(channel)) { + errorMessage.append(String.format(ERROR_TEMPLATE, HOST_AND_CHANNEL_NAME)).append(System.lineSeparator()); } if (errorMessage.length() != 0) { diff --git a/client/src/main/java/info/frostfs/sdk/jdo/ECDsa.java b/client/src/main/java/info/frostfs/sdk/jdo/ECDsa.java index d328c10..8e86d7c 100644 --- a/client/src/main/java/info/frostfs/sdk/jdo/ECDsa.java +++ b/client/src/main/java/info/frostfs/sdk/jdo/ECDsa.java @@ -7,13 +7,15 @@ import java.security.PrivateKey; import static info.frostfs.sdk.KeyExtension.*; public class ECDsa { + private static final String ERROR_MESSAGE = "WIF is invalid"; + private final byte[] publicKeyByte; private final byte[] privateKeyByte; private final PrivateKey privateKey; public ECDsa(String wif) { if (StringUtils.isEmpty(wif)) { - throw new IllegalArgumentException("Wif is invalid"); + throw new IllegalArgumentException(ERROR_MESSAGE); } this.privateKeyByte = getPrivateKeyFromWIF(wif); diff --git a/client/src/main/java/info/frostfs/sdk/jdo/NetworkSettings.java b/client/src/main/java/info/frostfs/sdk/jdo/NetworkSettings.java index 1ed01a6..daf9981 100644 --- a/client/src/main/java/info/frostfs/sdk/jdo/NetworkSettings.java +++ b/client/src/main/java/info/frostfs/sdk/jdo/NetworkSettings.java @@ -5,21 +5,21 @@ import java.util.Map; public class NetworkSettings { - public Long auditFee; - public Long basicIncomeRate; - public Long containerFee; - public Long containerAliasFee; - public Long innerRingCandidateFee; - public Long withdrawFee; - public Long epochDuration; - public Long iRCandidateFee; - public Long maxObjectSize; - public Long maxECDataCount; - public Long maxECParityCount; - public Long withdrawalFee; - public Boolean homomorphicHashingDisabled; - public Boolean maintenanceModeAllowed; - public Map unnamedSettings = new HashMap<>(); + private Long auditFee; + private Long basicIncomeRate; + private Long containerFee; + private Long containerAliasFee; + private Long innerRingCandidateFee; + private Long withdrawFee; + private Long epochDuration; + private Long iRCandidateFee; + private Long maxObjectSize; + private Long maxECDataCount; + private Long maxECParityCount; + private Long withdrawalFee; + private Boolean homomorphicHashingDisabled; + private Boolean maintenanceModeAllowed; + private Map unnamedSettings = new HashMap<>(); public Long getAuditFee() { return auditFee; @@ -77,11 +77,11 @@ public class NetworkSettings { this.epochDuration = epochDuration; } - public long getiRCandidateFee() { + public long getIRCandidateFee() { return iRCandidateFee; } - public void setiRCandidateFee(long iRCandidateFee) { + public void setIRCandidateFee(long iRCandidateFee) { this.iRCandidateFee = iRCandidateFee; } diff --git a/client/src/main/java/info/frostfs/sdk/jdo/PutObjectParameters.java b/client/src/main/java/info/frostfs/sdk/jdo/PutObjectParameters.java index 3c4ecdd..b091cb9 100644 --- a/client/src/main/java/info/frostfs/sdk/jdo/PutObjectParameters.java +++ b/client/src/main/java/info/frostfs/sdk/jdo/PutObjectParameters.java @@ -1,6 +1,7 @@ package info.frostfs.sdk.jdo; import info.frostfs.sdk.dto.object.ObjectHeader; +import info.frostfs.sdk.dto.session.SessionToken; import java.io.FileInputStream; @@ -8,11 +9,18 @@ import static java.util.Objects.isNull; public class PutObjectParameters { private static final String ERROR_TEMPLATE = "%s value cannot be null."; + private static final String HEADER_NAME = "Header"; + private static final String PAYLOAD_NAME = "Payload"; - public ObjectHeader header; - public FileInputStream payload; - public boolean clientCut; - public int bufferMaxSize; + private ObjectHeader header; + private FileInputStream payload; + private boolean clientCut; + private int bufferMaxSize; + private byte[] customerBuffer; + private SessionToken sessionToken; + private int maxObjectSizeCache; + private long currentStreamPosition; + private long fullLength; public PutObjectParameters(ObjectHeader header, FileInputStream payload, boolean clientCut, int bufferMaxSize) { this.header = header; @@ -30,6 +38,46 @@ public class PutObjectParameters { validate(); } + public byte[] getCustomerBuffer() { + return customerBuffer; + } + + public void setCustomerBuffer(byte[] customerBuffer) { + this.customerBuffer = customerBuffer; + } + + public SessionToken getSessionToken() { + return sessionToken; + } + + public void setSessionToken(SessionToken sessionToken) { + this.sessionToken = sessionToken; + } + + public int getMaxObjectSizeCache() { + return maxObjectSizeCache; + } + + public void setMaxObjectSizeCache(int maxObjectSizeCache) { + this.maxObjectSizeCache = maxObjectSizeCache; + } + + public long getCurrentStreamPosition() { + return currentStreamPosition; + } + + public void setCurrentStreamPosition(long currentStreamPosition) { + this.currentStreamPosition = currentStreamPosition; + } + + public long getFullLength() { + return fullLength; + } + + public void setFullLength(long fullLength) { + this.fullLength = fullLength; + } + public ObjectHeader getHeader() { return header; } @@ -66,11 +114,11 @@ public class PutObjectParameters { StringBuilder errorMessage = new StringBuilder(); if (isNull(header)) { - errorMessage.append(String.format(ERROR_TEMPLATE, "Header")).append(System.lineSeparator()); + errorMessage.append(String.format(ERROR_TEMPLATE, HEADER_NAME)).append(System.lineSeparator()); } if (isNull(payload)) { - errorMessage.append(String.format(ERROR_TEMPLATE, "Payload")).append(System.lineSeparator()); + errorMessage.append(String.format(ERROR_TEMPLATE, PAYLOAD_NAME)).append(System.lineSeparator()); } if (errorMessage.length() != 0) { diff --git a/client/src/main/java/info/frostfs/sdk/jdo/PutObjectResult.java b/client/src/main/java/info/frostfs/sdk/jdo/PutObjectResult.java new file mode 100644 index 0000000..24ee14b --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/jdo/PutObjectResult.java @@ -0,0 +1,21 @@ +package info.frostfs.sdk.jdo; + +import info.frostfs.sdk.dto.object.ObjectId; + +public class PutObjectResult { + private final ObjectId objectId; + private final int objectSize; + + public PutObjectResult(ObjectId objectId, int objectSize) { + this.objectId = objectId; + this.objectSize = objectSize; + } + + public ObjectId getObjectId() { + return objectId; + } + + public int getObjectSize() { + return objectSize; + } +} diff --git a/client/src/main/java/info/frostfs/sdk/jdo/WaitParameters.java b/client/src/main/java/info/frostfs/sdk/jdo/WaitParameters.java new file mode 100644 index 0000000..0aa8c47 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/jdo/WaitParameters.java @@ -0,0 +1,34 @@ +package info.frostfs.sdk.jdo; + +import java.time.Duration; +import java.time.LocalDateTime; + +public class WaitParameters { + private static final Duration DEFAULT_TIMEOUT = Duration.ofSeconds(120); + private static final Duration DEFAULT_POLL_INTERVAL = Duration.ofSeconds(5); + + private final Duration timeout; + private final Duration pollInterval; + + public WaitParameters() { + this.timeout = DEFAULT_TIMEOUT; + this.pollInterval = DEFAULT_POLL_INTERVAL; + } + + public WaitParameters(Duration timeout, Duration pollInterval) { + this.timeout = timeout; + this.pollInterval = pollInterval; + } + + public Duration getTimeout() { + return timeout; + } + + public Duration getPollInterval() { + return pollInterval; + } + + public LocalDateTime getDeadline() { + return LocalDateTime.now().plus(timeout); + } +} diff --git a/client/src/main/java/info/frostfs/sdk/services/ObjectClient.java b/client/src/main/java/info/frostfs/sdk/services/ObjectClient.java index 423e938..3a38295 100644 --- a/client/src/main/java/info/frostfs/sdk/services/ObjectClient.java +++ b/client/src/main/java/info/frostfs/sdk/services/ObjectClient.java @@ -14,7 +14,9 @@ public interface ObjectClient { ObjectId putObject(PutObjectParameters parameters); + ObjectId putSingleObject(ObjectFrostFS objectFrostFS); + void deleteObject(ContainerId containerId, ObjectId objectId); - Iterable searchObjects(ContainerId cid, ObjectFilter... filters); + Iterable searchObjects(ContainerId cid, ObjectFilter... filters); } diff --git a/client/src/main/java/info/frostfs/sdk/services/SessionClient.java b/client/src/main/java/info/frostfs/sdk/services/SessionClient.java index 16e0da0..e9b197c 100644 --- a/client/src/main/java/info/frostfs/sdk/services/SessionClient.java +++ b/client/src/main/java/info/frostfs/sdk/services/SessionClient.java @@ -1,6 +1,6 @@ package info.frostfs.sdk.services; -import info.frostfs.sdk.dto.SessionToken; +import info.frostfs.sdk.dto.session.SessionToken; public interface SessionClient { SessionToken createSession(long expiration); diff --git a/client/src/main/java/info/frostfs/sdk/services/SessionTools.java b/client/src/main/java/info/frostfs/sdk/services/SessionTools.java new file mode 100644 index 0000000..7976592 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/services/SessionTools.java @@ -0,0 +1,9 @@ +package info.frostfs.sdk.services; + +import frostfs.session.Types; +import info.frostfs.sdk.dto.session.SessionToken; +import info.frostfs.sdk.jdo.ClientEnvironment; + +public interface SessionTools { + Types.SessionToken getOrCreateSession(SessionToken token, ClientEnvironment env); +} diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/ContainerClientImpl.java b/client/src/main/java/info/frostfs/sdk/services/impl/ContainerClientImpl.java index 5c0e7af..db7c0bf 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/ContainerClientImpl.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/ContainerClientImpl.java @@ -2,48 +2,60 @@ package info.frostfs.sdk.services.impl; import frostfs.container.ContainerServiceGrpc; import frostfs.container.Service; +import frostfs.refs.Types; import info.frostfs.sdk.dto.container.Container; import info.frostfs.sdk.dto.container.ContainerId; +import info.frostfs.sdk.dto.session.SessionToken; +import info.frostfs.sdk.enums.StatusCode; +import info.frostfs.sdk.enums.WaitExpects; +import info.frostfs.sdk.exceptions.ResponseException; +import info.frostfs.sdk.exceptions.TimeoutException; import info.frostfs.sdk.jdo.ClientEnvironment; -import info.frostfs.sdk.mappers.OwnerIdMapper; -import info.frostfs.sdk.mappers.VersionMapper; +import info.frostfs.sdk.jdo.WaitParameters; import info.frostfs.sdk.mappers.container.ContainerIdMapper; import info.frostfs.sdk.mappers.container.ContainerMapper; +import info.frostfs.sdk.mappers.netmap.VersionMapper; +import info.frostfs.sdk.mappers.object.OwnerIdMapper; import info.frostfs.sdk.services.ContainerClient; import info.frostfs.sdk.services.ContextAccessor; import info.frostfs.sdk.tools.RequestConstructor; import info.frostfs.sdk.tools.RequestSigner; import info.frostfs.sdk.tools.Verifier; +import info.frostfs.sdk.utils.WaitUtil; +import java.time.LocalDateTime; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import static java.util.Objects.isNull; public class ContainerClientImpl extends ContextAccessor implements ContainerClient { + private static final String ERROR_CONTAINER_ID_MISSING = "ContainerId is not present"; + private static final String ERROR_CONTAINER_MISSING = "Container is not present"; + private final ContainerServiceGrpc.ContainerServiceBlockingStub serviceBlockingStub; + private final SessionToolsImpl sessionTools; public ContainerClientImpl(ClientEnvironment clientEnvironment) { super(clientEnvironment); this.serviceBlockingStub = ContainerServiceGrpc.newBlockingStub(clientEnvironment.getChannel()); + this.sessionTools = new SessionToolsImpl(clientEnvironment); + } + + public frostfs.session.Types.SessionToken getOrCreateSession(SessionToken sessionToken) { + return sessionTools.getOrCreateSession(sessionToken, getContext()); } @Override public Container getContainer(ContainerId cid) { if (isNull(cid)) { - throw new IllegalArgumentException("ContainerId is not present"); + throw new IllegalArgumentException(ERROR_CONTAINER_ID_MISSING); } - var body = Service.GetRequest.Body.newBuilder() - .setContainerId(ContainerIdMapper.toGrpcMessage(cid)) - .build(); - var request = Service.GetRequest.newBuilder() - .setBody(body); + var request = createGetRequest(ContainerIdMapper.toGrpcMessage(cid), null); - RequestConstructor.addDefaultMetaHeader(request); - RequestSigner.sign(request, getContext().getKey()); - - var response = serviceBlockingStub.get(request.build()); + var response = serviceBlockingStub.get(request); Verifier.checkResponse(response); return ContainerMapper.toModel(response.getBody().getContainer()); @@ -51,16 +63,9 @@ public class ContainerClientImpl extends ContextAccessor implements ContainerCli @Override public List listContainers() { - var body = Service.ListRequest.Body.newBuilder() - .setOwnerId(OwnerIdMapper.toGrpcMessage(getContext().getOwnerId())) - .build(); - var request = Service.ListRequest.newBuilder() - .setBody(body); + var request = createListRequest(null); - RequestConstructor.addDefaultMetaHeader(request); - RequestSigner.sign(request, getContext().getKey()); - - var response = serviceBlockingStub.list(request.build()); + var response = serviceBlockingStub.list(request); Verifier.checkResponse(response); @@ -72,51 +77,155 @@ public class ContainerClientImpl extends ContextAccessor implements ContainerCli @Override public ContainerId createContainer(Container container) { if (isNull(container)) { - throw new IllegalArgumentException("Container is not present"); + throw new IllegalArgumentException(ERROR_CONTAINER_MISSING); } var grpcContainer = ContainerMapper.toGrpcMessage(container); - grpcContainer = grpcContainer.toBuilder() - .setOwnerId(OwnerIdMapper.toGrpcMessage(getContext().getOwnerId())) - .setVersion(VersionMapper.toGrpcMessage(getContext().getVersion())) - .build(); + var request = createPutRequest(grpcContainer, null); - var body = Service.PutRequest.Body.newBuilder() - .setContainer(grpcContainer) - .setSignature(RequestSigner.signRFC6979(getContext().getKey(), grpcContainer)) - .build(); - var request = Service.PutRequest.newBuilder() - .setBody(body); - - RequestConstructor.addDefaultMetaHeader(request); - RequestSigner.sign(request, getContext().getKey()); - - var response = serviceBlockingStub.put(request.build()); + var response = serviceBlockingStub.put(request); Verifier.checkResponse(response); + + waitForContainer(WaitExpects.EXISTS, response.getBody().getContainerId(), null); + return new ContainerId(response.getBody().getContainerId().getValue().toByteArray()); } @Override public void deleteContainer(ContainerId cid) { if (isNull(cid)) { - throw new IllegalArgumentException("ContainerId is not present"); + throw new IllegalArgumentException(ERROR_CONTAINER_ID_MISSING); } var grpcContainerId = ContainerIdMapper.toGrpcMessage(cid); + var request = createDeleteRequest(grpcContainerId, null); + var response = serviceBlockingStub.delete(request); + + Verifier.checkResponse(response); + + waitForContainer(WaitExpects.REMOVED, request.getBody().getContainerId(), null); + } + + private void waitForContainer(WaitExpects expect, Types.ContainerID id, WaitParameters waitParams) { + var request = createGetRequest(id, null); + + waitParams = isNull(waitParams) ? new WaitParameters() : waitParams; + var deadLine = waitParams.getDeadline(); + + while (true) { + try { + var response = serviceBlockingStub.get(request); + Verifier.checkResponse(response); + + if (expect == WaitExpects.EXISTS) { + break; + } + + if (LocalDateTime.now().isAfter(deadLine)) { + throw new TimeoutException(); + } + + WaitUtil.sleep(waitParams.getPollInterval().toMillis()); + } catch (ResponseException exp) { + if (LocalDateTime.now().isAfter(deadLine)) { + throw new TimeoutException(); + } + + if (exp.getStatus().getCode() != StatusCode.CONTAINER_NOT_FOUND) { + throw exp; + } + + if (expect == WaitExpects.REMOVED) { + break; + } + + WaitUtil.sleep(waitParams.getPollInterval().toMillis()); + } + } + } + + + private Service.GetRequest createGetRequest(Types.ContainerID cid, + Map xHeaders) { + var body = Service.GetRequest.Body.newBuilder() + .setContainerId(cid) + .build(); + var request = Service.GetRequest.newBuilder() + .setBody(body); + + RequestConstructor.addMetaHeader(request, xHeaders); + RequestSigner.sign(request, getContext().getKey()); + + return request.build(); + } + + private Service.ListRequest createListRequest(Map xHeaders) { + var body = Service.ListRequest.Body.newBuilder() + .setOwnerId(OwnerIdMapper.toGrpcMessage(getContext().getOwnerId())) + .build(); + var request = Service.ListRequest.newBuilder() + .setBody(body); + + RequestConstructor.addMetaHeader(request, xHeaders); + RequestSigner.sign(request, getContext().getKey()); + + return request.build(); + } + + private Service.PutRequest createPutRequest(frostfs.container.Types.Container container, + Map xHeaders) { + container = container.toBuilder() + .setOwnerId(OwnerIdMapper.toGrpcMessage(getContext().getOwnerId())) + .setVersion(VersionMapper.toGrpcMessage(getContext().getVersion())) + .build(); + + var body = Service.PutRequest.Body.newBuilder() + .setContainer(container) + .setSignature(RequestSigner.signRFC6979(getContext().getKey(), container)) + .build(); + var request = Service.PutRequest.newBuilder() + .setBody(body); + + var sessionToken = getOrCreateSession(null); + + sessionToken = RequestConstructor.createContainerTokenContext( + sessionToken, + null, + frostfs.session.Types.ContainerSessionContext.Verb.PUT, + container.getOwnerId(), + getContext().getKey() + ); + + RequestConstructor.addMetaHeader(request, xHeaders, sessionToken); + RequestSigner.sign(request, getContext().getKey()); + + return request.build(); + } + + private Service.DeleteRequest createDeleteRequest(Types.ContainerID cid, + Map xHeaders) { var body = Service.DeleteRequest.Body.newBuilder() - .setContainerId(grpcContainerId) - .setSignature(RequestSigner.signRFC6979(getContext().getKey(), grpcContainerId.getValue())) + .setContainerId(cid) + .setSignature(RequestSigner.signRFC6979(getContext().getKey(), cid.getValue())) .build(); var request = Service.DeleteRequest.newBuilder() .setBody(body); - RequestConstructor.addDefaultMetaHeader(request); + var sessionToken = getOrCreateSession(null); + + sessionToken = RequestConstructor.createContainerTokenContext( + sessionToken, + null, + frostfs.session.Types.ContainerSessionContext.Verb.DELETE, + null, + getContext().getKey() + ); + + RequestConstructor.addMetaHeader(request, xHeaders, sessionToken); RequestSigner.sign(request, getContext().getKey()); - var response = serviceBlockingStub.delete(request.build()); - - Verifier.checkResponse(response); + return request.build(); } } diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/NetmapClientImpl.java b/client/src/main/java/info/frostfs/sdk/services/impl/NetmapClientImpl.java index 3a3d1ca..04109ad 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/NetmapClientImpl.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/NetmapClientImpl.java @@ -67,7 +67,7 @@ public class NetmapClientImpl extends ContextAccessor implements NetmapClient { settings.setEpochDuration(getLongValue(valueBytes)); break; case "InnerRingCandidateFee": - settings.setiRCandidateFee(getLongValue(valueBytes)); + settings.setIRCandidateFee(getLongValue(valueBytes)); break; case "MaxECDataCount": settings.setMaxECDataCount(getLongValue(valueBytes)); @@ -114,10 +114,9 @@ public class NetmapClientImpl extends ContextAccessor implements NetmapClient { @Override public NodeInfo getLocalNodeInfo() { - var request = Service.LocalNodeInfoRequest.newBuilder() - .setBody(Service.LocalNodeInfoRequest.Body.newBuilder().build()); + var request = Service.LocalNodeInfoRequest.newBuilder(); - RequestConstructor.addDefaultMetaHeader(request); + RequestConstructor.addMetaHeader(request); sign(request, getContext().getKey()); var response = netmapServiceClient.localNodeInfo(request.build()); @@ -127,10 +126,9 @@ public class NetmapClientImpl extends ContextAccessor implements NetmapClient { } public Service.NetworkInfoResponse getNetworkInfo() { - var request = Service.NetworkInfoRequest.newBuilder() - .setBody(Service.NetworkInfoRequest.Body.newBuilder().build()); + var request = Service.NetworkInfoRequest.newBuilder(); - RequestConstructor.addDefaultMetaHeader(request); + RequestConstructor.addMetaHeader(request); sign(request, getContext().getKey()); var response = netmapServiceClient.networkInfo(request.build()); @@ -142,10 +140,9 @@ public class NetmapClientImpl extends ContextAccessor implements NetmapClient { @Override public NetmapSnapshot getNetmapSnapshot() { - var request = Service.NetmapSnapshotRequest.newBuilder() - .setBody(Service.NetmapSnapshotRequest.Body.newBuilder().build()); + var request = Service.NetmapSnapshotRequest.newBuilder(); - RequestConstructor.addDefaultMetaHeader(request); + RequestConstructor.addMetaHeader(request); sign(request, getContext().getKey()); var response = netmapServiceClient.netmapSnapshot(request.build()); diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/ObjectClientImpl.java b/client/src/main/java/info/frostfs/sdk/services/impl/ObjectClientImpl.java index 69046d6..736189e 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/ObjectClientImpl.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/ObjectClientImpl.java @@ -6,21 +6,22 @@ import frostfs.object.ObjectServiceGrpc; import frostfs.object.Service; import frostfs.refs.Types; import info.frostfs.sdk.constants.AppConst; -import info.frostfs.sdk.dto.Split; import info.frostfs.sdk.dto.container.ContainerId; import info.frostfs.sdk.dto.object.*; +import info.frostfs.sdk.dto.session.SessionToken; +import info.frostfs.sdk.enums.ObjectType; import info.frostfs.sdk.jdo.ClientEnvironment; import info.frostfs.sdk.jdo.PutObjectParameters; -import info.frostfs.sdk.mappers.OwnerIdMapper; -import info.frostfs.sdk.mappers.VersionMapper; +import info.frostfs.sdk.jdo.PutObjectResult; import info.frostfs.sdk.mappers.container.ContainerIdMapper; import info.frostfs.sdk.mappers.object.ObjectFilterMapper; import info.frostfs.sdk.mappers.object.ObjectFrostFSMapper; import info.frostfs.sdk.mappers.object.ObjectHeaderMapper; import info.frostfs.sdk.mappers.object.ObjectIdMapper; +import info.frostfs.sdk.mappers.session.SessionMapper; import info.frostfs.sdk.services.ContextAccessor; import info.frostfs.sdk.services.ObjectClient; -import info.frostfs.sdk.services.impl.rwhelper.ObjectReader; +import info.frostfs.sdk.services.impl.rwhelper.ObjectReaderImpl; import info.frostfs.sdk.services.impl.rwhelper.ObjectWriter; import info.frostfs.sdk.services.impl.rwhelper.SearchReader; import info.frostfs.sdk.tools.RequestConstructor; @@ -30,44 +31,42 @@ import org.apache.commons.collections4.CollectionUtils; import java.io.FileInputStream; import java.io.IOException; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import static info.frostfs.sdk.Helper.getSha256; -import static info.frostfs.sdk.tools.RequestConstructor.addObjectSessionToken; import static info.frostfs.sdk.tools.RequestSigner.sign; -import static java.util.Objects.nonNull; +import static java.util.Objects.isNull; public class ObjectClientImpl extends ContextAccessor implements ObjectClient { - private static final String ERROR_PAYLOAD = "PayloadLength must be specified"; + private static final String ERROR_PARAMS_MISSING = "Params is not present"; private final ObjectServiceGrpc.ObjectServiceBlockingStub objectServiceBlockingClient; private final ObjectServiceGrpc.ObjectServiceStub objectServiceClient; private final ObjectToolsImpl objectToolsImpl; + private final SessionToolsImpl sessionTools; public ObjectClientImpl(ClientEnvironment clientEnvironment) { super(clientEnvironment); this.objectServiceBlockingClient = ObjectServiceGrpc.newBlockingStub(getContext().getChannel()); this.objectServiceClient = ObjectServiceGrpc.newStub(getContext().getChannel()); this.objectToolsImpl = new ObjectToolsImpl(clientEnvironment); + this.sessionTools = new SessionToolsImpl(clientEnvironment); + } + + public frostfs.session.Types.SessionToken getOrCreateSession(SessionToken sessionToken) { + return sessionTools.getOrCreateSession(sessionToken, getContext()); } @Override public ObjectHeader getObjectHead(ContainerId cid, ObjectId oid) { - var address = Types.Address.newBuilder() - .setContainerId(ContainerIdMapper.toGrpcMessage(cid)) - .setObjectId(ObjectIdMapper.toGrpcMessage(oid)) - .build(); - var body = Service.HeadRequest.Body.newBuilder() - .setAddress(address) - .build(); - var request = Service.HeadRequest.newBuilder() - .setBody(body); + if (isNull(cid) || isNull(oid)) { + throw new IllegalArgumentException(ERROR_PARAMS_MISSING); + } - RequestConstructor.addDefaultMetaHeader(request); - sign(request, getContext().getKey()); + var request = createHeadRequest(ContainerIdMapper.toGrpcMessage(cid), ObjectIdMapper.toGrpcMessage(oid)); + + var response = objectServiceBlockingClient.head(request); - var response = objectServiceBlockingClient.head(request.build()); Verifier.checkResponse(response); return ObjectHeaderMapper.toModel(response.getBody().getHeader().getHeader()); @@ -75,66 +74,25 @@ public class ObjectClientImpl extends ContextAccessor implements ObjectClient { @Override public ObjectFrostFS getObject(ContainerId cid, ObjectId oid) { - var sessionToken = getContext().getFrostFSClient().createSessionInternal(-1); + var request = createGetRequest(ContainerIdMapper.toGrpcMessage(cid), ObjectIdMapper.toGrpcMessage(oid)); - var address = Types.Address.newBuilder() - .setContainerId(ContainerIdMapper.toGrpcMessage(cid)) - .setObjectId(ObjectIdMapper.toGrpcMessage(oid)) - .build(); - var body = Service.GetRequest.Body.newBuilder() - .setAddress(address) - .build(); - var request = Service.GetRequest.newBuilder() - .setBody(body); - - RequestConstructor.addDefaultMetaHeader(request); - addObjectSessionToken( - request, sessionToken, ContainerIdMapper.toGrpcMessage(cid), ObjectIdMapper.toGrpcMessage(oid), - frostfs.session.Types.ObjectSessionContext.Verb.GET, getContext().getKey() - ); - sign(request, getContext().getKey()); - - var obj = getObject(request.build()); - return ObjectFrostFSMapper.toModel(obj); + return getObject(request); } - @Override public void deleteObject(ContainerId cid, ObjectId oid) { - var address = Types.Address.newBuilder() - .setContainerId(ContainerIdMapper.toGrpcMessage(cid)) - .setObjectId(ObjectIdMapper.toGrpcMessage(oid)) - .build(); - var body = Service.DeleteRequest.Body.newBuilder() - .setAddress(address) - .build(); - var request = Service.DeleteRequest.newBuilder() - .setBody(body); + var request = createDeleteRequest(ContainerIdMapper.toGrpcMessage(cid), ObjectIdMapper.toGrpcMessage(oid)); - RequestConstructor.addDefaultMetaHeader(request); - sign(request, getContext().getKey()); + var response = objectServiceBlockingClient.delete(request); - var response = objectServiceBlockingClient.delete(request.build()); Verifier.checkResponse(response); } @Override public Iterable searchObjects(ContainerId cid, ObjectFilter... filters) { - var body = Service.SearchRequest.Body.newBuilder() - .setContainerId(ContainerIdMapper.toGrpcMessage(cid)) - .setVersion(1);// TODO: clarify this param + var request = createSearchRequest(ContainerIdMapper.toGrpcMessage(cid), filters); - for (ObjectFilter filter : filters) { - body.addFilters(ObjectFilterMapper.toGrpcMessage(filter)); - } - - var request = Service.SearchRequest.newBuilder() - .setBody(body.build()); - - RequestConstructor.addDefaultMetaHeader(request); - sign(request, getContext().getKey()); - - var objectsIds = searchObjects(request.build()); + var objectsIds = searchObjects(request); return Iterables.transform(objectsIds, input -> new ObjectId(input.getValue().toByteArray())); } @@ -143,179 +101,188 @@ public class ObjectClientImpl extends ContextAccessor implements ObjectClient { public ObjectId putObject(PutObjectParameters parameters) { parameters.validate(); - return parameters.clientCut ? putClientCutObject(parameters) : putStreamObject(parameters); + if (parameters.isClientCut()) { + return putClientCutObject(parameters); + } + + if (parameters.getHeader().getPayloadLength() > 0) { + parameters.setFullLength(parameters.getHeader().getPayloadLength()); + } else { + parameters.setFullLength(getStreamSize(parameters.getPayload())); + } + + return putStreamObject(parameters).getObjectId(); } + @Override public ObjectId putSingleObject(ObjectFrostFS modelObject) { - var sessionToken = getContext().getFrostFSClient().createSessionInternal(-1); - var grpcObject = objectToolsImpl.createObject(modelObject); - var request = Service.PutSingleRequest.newBuilder() - .setBody(Service.PutSingleRequest.Body.newBuilder().setObject(grpcObject).build()); + var request = createPutSingleRequest(grpcObject); - - RequestConstructor.addDefaultMetaHeader(request); - addObjectSessionToken( - request, sessionToken, grpcObject.getHeader().getContainerId(), grpcObject.getObjectId(), - frostfs.session.Types.ObjectSessionContext.Verb.PUT, getContext().getKey() - ); - sign(request, getContext().getKey()); - - var response = objectServiceBlockingClient.putSingle(request.build()); + var response = objectServiceBlockingClient.putSingle(request); Verifier.checkResponse(response); return new ObjectId(grpcObject.getObjectId().getValue().toByteArray()); } - private frostfs.object.Types.Object getObject(Service.GetRequest request) { - var iterator = getObjectInit(request); - var obj = iterator.readHeader(); - var payload = new byte[Math.toIntExact(obj.getHeader().getPayloadLength())]; - var offset = 0; - var chunk = iterator.readChunk(); + private ObjectFrostFS getObject(Service.GetRequest request) { + var reader = getObjectInit(request); - while (nonNull(chunk)) { - System.arraycopy(chunk, 0, payload, offset, chunk.length); - offset += chunk.length; - chunk = iterator.readChunk(); - } + var grpcObject = reader.readHeader(); + var modelObject = ObjectFrostFSMapper.toModel(grpcObject); - return obj.toBuilder().setPayload(ByteString.copyFrom(payload)).build(); + modelObject.setObjectReader(reader); + + return modelObject; } - private ObjectReader getObjectInit(Service.GetRequest initRequest) { + private ObjectReaderImpl getObjectInit(Service.GetRequest initRequest) { if (initRequest.getSerializedSize() == 0) { throw new IllegalArgumentException(initRequest.getClass().getName()); } - return new ObjectReader(objectServiceBlockingClient.get(initRequest)); + return new ObjectReaderImpl(objectServiceBlockingClient.get(initRequest)); } - private ObjectId putStreamObject(PutObjectParameters parameters) { - var header = parameters.getHeader(); + private PutObjectResult putStreamObject(PutObjectParameters parameters) { + var chunkSize = parameters.getBufferMaxSize() > 0 ? parameters.getBufferMaxSize() : AppConst.OBJECT_CHUNK_SIZE; - var sessionToken = getContext().getFrostFSClient().createSessionInternal(-1); + var restBytes = parameters.getFullLength() - parameters.getCurrentStreamPosition(); - var hdr = ObjectHeaderMapper.toGrpcMessage(header); - hdr = hdr.toBuilder() - .setOwnerId(OwnerIdMapper.toGrpcMessage(getContext().getOwnerId())) - .setVersion(VersionMapper.toGrpcMessage(getContext().getVersion())) - .build(); + chunkSize = (int) Math.min(restBytes, chunkSize); - var oid = Types.ObjectID.newBuilder().setValue(getSha256(hdr)).build(); + byte[] chunkBuffer = parameters.getCustomerBuffer() != null + ? parameters.getCustomerBuffer() + : new byte[chunkSize];//todo change to pool - var initRequest = Service.PutRequest.newBuilder() - .setBody( - Service.PutRequest.Body.newBuilder() - .setInit( - Service.PutRequest.Body.Init.newBuilder().setHeader(hdr).build() - ).build() - ); + var sentBytes = 0; - RequestConstructor.addDefaultMetaHeader(initRequest); - addObjectSessionToken( - initRequest, sessionToken, hdr.getContainerId(), oid, - frostfs.session.Types.ObjectSessionContext.Verb.PUT, getContext().getKey() - ); - sign(initRequest, getContext().getKey()); + // 0 means no limit from client, so server side cut is performed + var objectLimitSize = parameters.isClientCut() ? parameters.getMaxObjectSizeCache() : 0; + var stream = getUploadStream(parameters); - var writer = putObjectInit(initRequest.build()); + while (objectLimitSize == 0 || sentBytes < objectLimitSize) { + // send chunks limited to default or user's settings + var bufferSize = objectLimitSize > 0 ? Math.min(objectLimitSize - sentBytes, chunkSize) : chunkSize; - var bufferSize = parameters.getBufferMaxSize() > 0 ? parameters.getBufferMaxSize() : AppConst.OBJECT_CHUNK_SIZE; - bufferSize = (int) Math.min(getStreamSize(parameters.getPayload()), bufferSize); - bufferSize = header.getPayloadLength() > 0 ? (int) Math.min(header.getPayloadLength(), bufferSize) : bufferSize; + var bytesCount = readNBytes(parameters.getPayload(), chunkBuffer, bufferSize); - var buffer = new byte[bufferSize]; - while (true) { - var bytesCount = readNBytes(parameters.getPayload(), buffer, bufferSize); - if (bytesCount <= 0) { + if (bytesCount == 0) { break; } - var chunkRequest = Service.PutRequest.newBuilder(initRequest.build()) - .setBody( - Service.PutRequest.Body.newBuilder() - .setChunk(ByteString.copyFrom(Arrays.copyOfRange(buffer, 0, bytesCount))) - .build() - ) + sentBytes += bytesCount; + + var body = Service.PutRequest.Body.newBuilder() + .setChunk(ByteString.copyFrom(chunkBuffer, 0, bytesCount)) + .build(); + var chunkRequest = Service.PutRequest.newBuilder() + .setBody(body) .clearVerifyHeader(); + sign(chunkRequest, getContext().getKey()); - writer.write(chunkRequest.build()); + + stream.write(chunkRequest.build()); } - var response = writer.complete(); + var response = stream.complete(); Verifier.checkResponse(response); - return new ObjectId(response.getBody().getObjectId().getValue().toByteArray()); + var objectId = new ObjectId(response.getBody().getObjectId().getValue().toByteArray()); + return new PutObjectResult(objectId, sentBytes); } private ObjectId putClientCutObject(PutObjectParameters parameters) { var header = parameters.getHeader(); + var tokenRaw = getOrCreateSession(parameters.getSessionToken()); + var token = new SessionToken(SessionMapper.serialize(tokenRaw)); + parameters.setSessionToken(token); - var networkSettings = getContext().getFrostFSClient().getNetworkSettings(); - var payloadSize = getStreamSize(parameters.getPayload()); - var objectSize = (int) Math.min(payloadSize, networkSettings.getMaxObjectSize()); - var fullLength = header.getPayloadLength() == 0 ? payloadSize : header.getPayloadLength(); - if (fullLength == 0) { - throw new IllegalArgumentException(ERROR_PAYLOAD); + var fullLength = header.getPayloadLength() == 0 + ? getStreamSize(parameters.getPayload()) + : header.getPayloadLength(); + + parameters.setFullLength(fullLength); + + if (parameters.getMaxObjectSizeCache() == 0) { + var networkSettings = getContext().getFrostFSClient().getNetworkSettings(); + parameters.setMaxObjectSizeCache((int) networkSettings.getMaxObjectSize()); } - var buffer = new byte[objectSize]; - var largeObject = new LargeObject(header.getContainerId()); - var split = new Split(); + var restBytes = fullLength - parameters.getCurrentStreamPosition(); + var objectSize = restBytes > 0 + ? Math.min(parameters.getMaxObjectSizeCache(), restBytes) + : parameters.getMaxObjectSizeCache(); - ObjectId objectId; - List sentObjectIds = new ArrayList<>(); - ObjectFrostFS currentObject; + //define collection capacity + var restPart = (restBytes % objectSize) > 0 ? 1 : 0; + var objectsCount = fullLength > 0 ? (int) (restBytes / objectSize) + restPart : 0; - while (true) { - var bytesCount = readNBytes(parameters.getPayload(), buffer, objectSize); - if (CollectionUtils.isNotEmpty(sentObjectIds)) { - split.setPrevious(sentObjectIds.get(sentObjectIds.size() - 1)); - } + List sentObjectIds = new ArrayList<>(objectsCount); - largeObject.appendBlock(buffer, bytesCount); + // keep attributes for the large object + var attributes = parameters.getHeader().getAttributes(); - currentObject = new ObjectFrostFS( - header.getContainerId(), - bytesCount < objectSize ? Arrays.copyOfRange(buffer, 0, bytesCount) : buffer - ); - currentObject.setSplit(split); + Split split = new Split(); + parameters.getHeader().setSplit(split); + parameters.getHeader().setAttributes(new ArrayList<>()); - if (largeObject.getPayloadLength() == fullLength) { - break; - } + // send all parts except the last one as separate Objects + while (restBytes > (long) parameters.getMaxObjectSizeCache()) { + var previous = CollectionUtils.isNotEmpty(sentObjectIds) + ? sentObjectIds.get(sentObjectIds.size() - 1) + : null; + split.setPrevious(previous); - objectId = putSingleObject(currentObject); + var result = putStreamObject(parameters); - sentObjectIds.add(objectId); + sentObjectIds.add(result.getObjectId()); + + restBytes -= result.getObjectSize(); } - if (CollectionUtils.isEmpty(sentObjectIds)) { - currentObject.addAttributes(parameters.getHeader().getAttributes()); - return putSingleObject(currentObject); + // send the last part and create linkObject + if (CollectionUtils.isNotEmpty(sentObjectIds)) { + var largeObjectHeader = + new ObjectHeader(header.getContainerId(), ObjectType.REGULAR, attributes, fullLength, null); + + split.setParentHeader(largeObjectHeader); + + var result = putStreamObject(parameters); + + sentObjectIds.add(result.getObjectId()); + + var linkObject = new LinkObject(header.getContainerId(), split.getSplitId(), largeObjectHeader); + linkObject.addChildren(sentObjectIds); + + putSingleObject(linkObject); + + return split.getParent(); } - largeObject.addAttributes(parameters.getHeader().getAttributes()); - largeObject.calculateHash(); + // We are here if the payload is placed to one Object. It means no cut action, just simple PUT. + var singlePartResult = putStreamObject(parameters); - currentObject.setParent(largeObject); + return singlePartResult.getObjectId(); + } - objectId = putSingleObject(currentObject); + private ObjectWriter getUploadStream(PutObjectParameters parameters) { + var header = parameters.getHeader(); - sentObjectIds.add(objectId); + header.setOwnerId(getContext().getOwnerId()); + header.setVersion(getContext().getVersion()); - var linkObject = new LinkObject(header.getContainerId(), split.getSplitId(), largeObject); - linkObject.addChildren(sentObjectIds); + var grpcHeader = ObjectHeaderMapper.toGrpcMessage(header); + grpcHeader = objectToolsImpl.updateSplitValues(grpcHeader, header.getSplit()); - linkObject.getHeader().getAttributes().clear(); + var oid = Types.ObjectID.newBuilder().setValue(getSha256(grpcHeader)).build(); - putSingleObject(linkObject); + var initRequest = createInitPutRequest(oid, grpcHeader); - return objectToolsImpl.calculateObjectId(largeObject.getHeader()); + return putObjectInit(initRequest); } private ObjectWriter putObjectInit(Service.PutRequest initRequest) { @@ -333,12 +300,12 @@ public class ObjectClientImpl extends ContextAccessor implements ObjectClient { var ids = reader.read(); List result = new ArrayList<>(); - while (nonNull(ids) && !ids.isEmpty()) { + while (CollectionUtils.isNotEmpty(ids)) { result.addAll(ids); ids = reader.read(); } - return result; + return result;//todo return yield } private SearchReader getSearchReader(Service.SearchRequest initRequest) { @@ -364,4 +331,168 @@ public class ObjectClientImpl extends ContextAccessor implements ObjectClient { throw new IllegalArgumentException(exp.getMessage()); } } + + private Service.HeadRequest createHeadRequest(Types.ContainerID cid, Types.ObjectID oid) { + var address = Types.Address.newBuilder() + .setContainerId(cid) + .setObjectId(oid) + .build(); + var body = Service.HeadRequest.Body.newBuilder() + .setAddress(address) + .build(); + var request = Service.HeadRequest.newBuilder() + .setBody(body); + + var sessionToken = getOrCreateSession(null); + + sessionToken = RequestConstructor.createObjectTokenContext( + sessionToken, + address, + frostfs.session.Types.ObjectSessionContext.Verb.HEAD, + getContext().getKey() + ); + + RequestConstructor.addMetaHeader(request, null, sessionToken); + sign(request, getContext().getKey()); + + return request.build(); + } + + private Service.GetRequest createGetRequest(Types.ContainerID cid, Types.ObjectID oid) { + var address = Types.Address.newBuilder() + .setContainerId(cid) + .setObjectId(oid) + .build(); + var body = Service.GetRequest.Body.newBuilder() + .setAddress(address) + .build(); + var request = Service.GetRequest.newBuilder() + .setBody(body); + + var sessionToken = getOrCreateSession(null); + + sessionToken = RequestConstructor.createObjectTokenContext( + sessionToken, + address, + frostfs.session.Types.ObjectSessionContext.Verb.GET, + getContext().getKey() + ); + + RequestConstructor.addMetaHeader(request, null, sessionToken); + sign(request, getContext().getKey()); + + return request.build(); + } + + private Service.DeleteRequest createDeleteRequest(Types.ContainerID cid, Types.ObjectID oid) { + var address = Types.Address.newBuilder() + .setContainerId(cid) + .setObjectId(oid) + .build(); + var body = Service.DeleteRequest.Body.newBuilder() + .setAddress(address) + .build(); + var request = Service.DeleteRequest.newBuilder() + .setBody(body); + + var sessionToken = getOrCreateSession(null); + + sessionToken = RequestConstructor.createObjectTokenContext( + sessionToken, + address, + frostfs.session.Types.ObjectSessionContext.Verb.DELETE, + getContext().getKey() + ); + + RequestConstructor.addMetaHeader(request, null, sessionToken); + sign(request, getContext().getKey()); + + return request.build(); + } + + private Service.SearchRequest createSearchRequest(Types.ContainerID cid, ObjectFilter... filters) { + var address = Types.Address.newBuilder() + .setContainerId(cid) + .build(); + + var body = Service.SearchRequest.Body.newBuilder() + .setContainerId(cid) + .setVersion(1);// TODO: clarify this param + + for (ObjectFilter filter : filters) { + body.addFilters(ObjectFilterMapper.toGrpcMessage(filter)); + } + + var request = Service.SearchRequest.newBuilder() + .setBody(body.build()); + + var sessionToken = getOrCreateSession(null); + + sessionToken = RequestConstructor.createObjectTokenContext( + sessionToken, + address, + frostfs.session.Types.ObjectSessionContext.Verb.SEARCH, + getContext().getKey() + ); + + RequestConstructor.addMetaHeader(request, null, sessionToken); + sign(request, getContext().getKey()); + + return request.build(); + } + + private Service.PutRequest createInitPutRequest(Types.ObjectID oid, frostfs.object.Types.Header header) { + var address = Types.Address.newBuilder() + .setContainerId(header.getContainerId()) + .setObjectId(oid) + .build(); + var init = Service.PutRequest.Body.Init.newBuilder() + .setHeader(header) + .build(); + var body = Service.PutRequest.Body.newBuilder() + .setInit(init) + .build(); + var request = Service.PutRequest.newBuilder() + .setBody(body); + + var sessionToken = getOrCreateSession(null); + + sessionToken = RequestConstructor.createObjectTokenContext( + sessionToken, + address, + frostfs.session.Types.ObjectSessionContext.Verb.PUT, + getContext().getKey() + ); + + RequestConstructor.addMetaHeader(request, null, sessionToken); + sign(request, getContext().getKey()); + + return request.build(); + } + + private Service.PutSingleRequest createPutSingleRequest(frostfs.object.Types.Object grpcObject) { + var address = Types.Address.newBuilder() + .setContainerId(grpcObject.getHeader().getContainerId()) + .setObjectId(grpcObject.getObjectId()) + .build(); + var body = Service.PutSingleRequest.Body.newBuilder() + .setObject(grpcObject) + .build(); + var request = Service.PutSingleRequest.newBuilder() + .setBody(body); + + var sessionToken = getOrCreateSession(null); + + sessionToken = RequestConstructor.createObjectTokenContext( + sessionToken, + address, + frostfs.session.Types.ObjectSessionContext.Verb.PUT, + getContext().getKey() + ); + + RequestConstructor.addMetaHeader(request, null, sessionToken); + sign(request, getContext().getKey()); + + return request.build(); + } } diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/ObjectToolsImpl.java b/client/src/main/java/info/frostfs/sdk/services/impl/ObjectToolsImpl.java index 2fb3b42..9730f34 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/ObjectToolsImpl.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/ObjectToolsImpl.java @@ -5,17 +5,21 @@ import frostfs.object.Types; import info.frostfs.sdk.dto.object.ObjectFrostFS; import info.frostfs.sdk.dto.object.ObjectHeader; import info.frostfs.sdk.dto.object.ObjectId; +import info.frostfs.sdk.dto.object.Split; import info.frostfs.sdk.jdo.ClientEnvironment; -import info.frostfs.sdk.mappers.OwnerIdMapper; -import info.frostfs.sdk.mappers.VersionMapper; +import info.frostfs.sdk.mappers.netmap.VersionMapper; import info.frostfs.sdk.mappers.object.ObjectHeaderMapper; import info.frostfs.sdk.mappers.object.ObjectIdMapper; +import info.frostfs.sdk.mappers.object.OwnerIdMapper; import info.frostfs.sdk.services.ContextAccessor; import info.frostfs.sdk.services.ToolsClient; -import org.apache.commons.collections4.ListUtils; +import org.apache.commons.collections4.CollectionUtils; + +import java.util.stream.Collectors; import static info.frostfs.sdk.Helper.getSha256; import static info.frostfs.sdk.tools.RequestSigner.signData; +import static java.util.Objects.isNull; import static java.util.Objects.nonNull; public class ObjectToolsImpl extends ContextAccessor implements ToolsClient { @@ -34,52 +38,29 @@ public class ObjectToolsImpl extends ContextAccessor implements ToolsClient { public ObjectId calculateObjectId(ObjectHeader header) { var grpcHeader = createHeader(header, new byte[]{}); + if (nonNull(header.getSplit())) { + grpcHeader = updateSplitValues(grpcHeader, header.getSplit()); + } + return ObjectIdMapper.toModel( frostfs.refs.Types.ObjectID.newBuilder().setValue(getSha256(grpcHeader)).build() ); } public Types.Object createObject(ObjectFrostFS objectFrostFs) { - var grpcHeaderBuilder = ObjectHeaderMapper.toGrpcMessage(objectFrostFs.getHeader()).toBuilder() - .setOwnerId(OwnerIdMapper.toGrpcMessage(getContext().getOwnerId())) - .setVersion(VersionMapper.toGrpcMessage(getContext().getVersion())) - .setPayloadLength(objectFrostFs.getPayload().length) - .setPayloadHash(sha256Checksum(objectFrostFs.getPayload())); + objectFrostFs.getHeader().setOwnerId(getContext().getOwnerId()); + objectFrostFs.getHeader().setVersion(getContext().getVersion()); + objectFrostFs.getHeader().setPayloadLength(objectFrostFs.getPayload().length); + + var grpcHeader = ObjectHeaderMapper.toGrpcMessage(objectFrostFs.getHeader()).toBuilder() + .setPayloadHash(sha256Checksum(objectFrostFs.getPayload())) + .build(); var split = objectFrostFs.getHeader().getSplit(); if (nonNull(split)) { - var splitGrpc = Types.Header.Split.newBuilder() - .setSplitId( - nonNull(split.getSplitId()) - ? ByteString.copyFrom(split.getSplitId().toBinary()) - : null - ); - - ListUtils.emptyIfNull(split.getChildren()).stream() - .map(ObjectIdMapper::toGrpcMessage) - .forEach(splitGrpc::addChildren); - - - if (nonNull(split.getParentHeader())) { - var grpcParentHeader = createHeader(split.getParentHeader(), new byte[]{}); - var parent = frostfs.refs.Types.ObjectID.newBuilder().setValue(getSha256(grpcParentHeader)).build(); - var parentSig = frostfs.refs.Types.Signature.newBuilder() - .setKey(ByteString.copyFrom(getContext().getKey().getPublicKeyByte())) - .setSign(ByteString.copyFrom(signData(getContext().getKey(), parent.toByteArray()))); - - splitGrpc.setParent(parent) - .setParentHeader(grpcParentHeader) - .setParentSignature(parentSig); - - split.setParent(ObjectIdMapper.toModel(parent)); - } - if (nonNull(split.getPrevious())) { - splitGrpc.setPrevious(ObjectIdMapper.toGrpcMessage(split.getPrevious())); - } - grpcHeaderBuilder.setSplit(splitGrpc); + grpcHeader = updateSplitValues(grpcHeader, split); } - var grpcHeader = grpcHeaderBuilder.build(); var objectId = frostfs.refs.Types.ObjectID.newBuilder().setValue(getSha256(grpcHeader)).build(); var sig = frostfs.refs.Types.Signature.newBuilder() .setKey(ByteString.copyFrom(getContext().getKey().getPublicKeyByte())) @@ -92,14 +73,48 @@ public class ObjectToolsImpl extends ContextAccessor implements ToolsClient { .build(); } + public Types.Header updateSplitValues(Types.Header grpcHeader, Split split) { + if (isNull(split)) { + return grpcHeader; + } + + var grpcSplit = grpcHeader.getSplit().toBuilder() + .setSplitId(ByteString.copyFrom(split.getSplitId().toBinary())); + + if (CollectionUtils.isNotEmpty(split.getChildren())) { + var grpcChildren = split.getChildren().stream() + .map(ObjectIdMapper::toGrpcMessage) + .collect(Collectors.toList()); + grpcSplit.addAllChildren(grpcChildren); + } + + if (nonNull(split.getParentHeader())) { + var grpcParentHeader = createHeader(split.getParentHeader(), new byte[]{}); + var parentObjectId = frostfs.refs.Types.ObjectID.newBuilder().setValue(getSha256(grpcParentHeader)).build(); + var signature = frostfs.refs.Types.Signature.newBuilder() + .setKey(ByteString.copyFrom(getContext().getKey().getPublicKeyByte())) + .setSign(ByteString.copyFrom(signData(getContext().getKey(), parentObjectId.toByteArray()))) + .build(); + + + grpcSplit + .setParent(parentObjectId) + .setParentHeader(grpcParentHeader) + .setParentSignature(signature); + + split.setParent(ObjectIdMapper.toModel(parentObjectId)); + } + + grpcSplit.setPrevious(ObjectIdMapper.toGrpcMessage(split.getPrevious())).build(); + return grpcHeader.toBuilder().setSplit(grpcSplit.build()).build(); + } + private Types.Header createHeader(ObjectHeader header, byte[] payload) { var grpcHeader = ObjectHeaderMapper.toGrpcMessage(header).toBuilder() .setOwnerId(OwnerIdMapper.toGrpcMessage(getContext().getOwnerId())) .setVersion(VersionMapper.toGrpcMessage(getContext().getVersion())); - if (header.getPayloadCheckSum() != null) { - grpcHeader.setPayloadHash(sha256Checksum(header.getPayloadCheckSum())); - } else if (payload != null) { + if (payload != null) { grpcHeader.setPayloadHash(sha256Checksum(payload)); } diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/SessionClientImpl.java b/client/src/main/java/info/frostfs/sdk/services/impl/SessionClientImpl.java index 137c178..8151fc9 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/SessionClientImpl.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/SessionClientImpl.java @@ -3,13 +3,14 @@ package info.frostfs.sdk.services.impl; import frostfs.session.Service; import frostfs.session.SessionServiceGrpc; import frostfs.session.Types; -import info.frostfs.sdk.dto.SessionToken; +import info.frostfs.sdk.dto.session.SessionToken; import info.frostfs.sdk.jdo.ClientEnvironment; -import info.frostfs.sdk.mappers.OwnerIdMapper; -import info.frostfs.sdk.mappers.SessionMapper; +import info.frostfs.sdk.mappers.object.OwnerIdMapper; +import info.frostfs.sdk.mappers.session.SessionMapper; import info.frostfs.sdk.services.ContextAccessor; import info.frostfs.sdk.services.SessionClient; import info.frostfs.sdk.tools.RequestConstructor; +import info.frostfs.sdk.tools.Verifier; import static info.frostfs.sdk.tools.RequestSigner.sign; @@ -25,7 +26,7 @@ public class SessionClientImpl extends ContextAccessor implements SessionClient public SessionToken createSession(long expiration) { var sessionToken = createSessionInternal(expiration); var token = SessionMapper.serialize(sessionToken); - return new SessionToken(new byte[]{}, token); + return new SessionToken(token); } public Types.SessionToken createSessionInternal(long expiration) { @@ -36,24 +37,26 @@ public class SessionClientImpl extends ContextAccessor implements SessionClient var request = Service.CreateRequest.newBuilder() .setBody(body); - RequestConstructor.addDefaultMetaHeader(request); + RequestConstructor.addMetaHeader(request); sign(request, getContext().getKey()); return createSession(request.build()); } private Types.SessionToken createSession(Service.CreateRequest request) { - var resp = serviceBlockingStub.create(request); + var response = serviceBlockingStub.create(request); + + Verifier.checkResponse(response); var lifetime = Types.SessionToken.Body.TokenLifetime.newBuilder() .setExp(request.getBody().getExpiration()) - .setIat(resp.getMetaHeader().getEpoch()) - .setNbf(resp.getMetaHeader().getEpoch()) + .setIat(response.getMetaHeader().getEpoch()) + .setNbf(response.getMetaHeader().getEpoch()) .build(); var body = Types.SessionToken.Body.newBuilder() - .setId(resp.getBody().getId()) - .setSessionKey(resp.getBody().getSessionKey()) + .setId(response.getBody().getId()) + .setSessionKey(response.getBody().getSessionKey()) .setOwnerId(request.getBody().getOwnerId()) .setLifetime(lifetime) .build(); diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/SessionToolsImpl.java b/client/src/main/java/info/frostfs/sdk/services/impl/SessionToolsImpl.java new file mode 100644 index 0000000..e0d3a06 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/services/impl/SessionToolsImpl.java @@ -0,0 +1,27 @@ +package info.frostfs.sdk.services.impl; + +import frostfs.session.Types; +import info.frostfs.sdk.dto.session.SessionToken; +import info.frostfs.sdk.jdo.ClientEnvironment; +import info.frostfs.sdk.mappers.session.SessionMapper; +import info.frostfs.sdk.services.ContextAccessor; +import info.frostfs.sdk.services.SessionTools; + +import static java.util.Objects.isNull; + +public class SessionToolsImpl extends ContextAccessor implements SessionTools { + + public SessionToolsImpl(ClientEnvironment clientEnvironment) { + super(clientEnvironment); + } + + @Override + public Types.SessionToken getOrCreateSession(SessionToken sessionToken, ClientEnvironment env) { + if (isNull(sessionToken)) { + return env.getFrostFSClient().createSessionInternal(-1); + } + + return SessionMapper.deserializeSessionToken(sessionToken.getToken()); + } + +} diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectReader.java b/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectReaderImpl.java similarity index 89% rename from client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectReader.java rename to client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectReaderImpl.java index d686921..b5f50d4 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectReader.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectReaderImpl.java @@ -2,17 +2,18 @@ package info.frostfs.sdk.services.impl.rwhelper; import frostfs.object.Service; import frostfs.object.Types; +import info.frostfs.sdk.dto.object.ObjectReader; import info.frostfs.sdk.tools.Verifier; import java.util.Iterator; -public class ObjectReader { +public class ObjectReaderImpl implements ObjectReader { public static final String ERROR_UNEXPECTED_STREAM = "unexpected end of stream"; public static final String ERROR_UNEXPECTED_MESSAGE_TYPE = "unexpected message type"; public Iterator call; - public ObjectReader(Iterator call) { + public ObjectReaderImpl(Iterator call) { this.call = call; } diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectWriter.java b/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectWriter.java index 7d6271b..110df20 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectWriter.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectWriter.java @@ -2,11 +2,13 @@ package info.frostfs.sdk.services.impl.rwhelper; import frostfs.object.ObjectServiceGrpc; import frostfs.object.Service; +import info.frostfs.sdk.utils.WaitUtil; import io.grpc.stub.StreamObserver; import static java.util.Objects.isNull; public class ObjectWriter { + private static final long POLL_INTERVAL = 10; private final StreamObserver requestObserver; private final PutResponseCallback responseObserver; @@ -29,7 +31,7 @@ public class ObjectWriter { requestObserver.onCompleted(); while (isNull(responseObserver.getResponse())) { - System.out.println("Waiting response"); + WaitUtil.sleep(POLL_INTERVAL); } return responseObserver.getResponse(); diff --git a/client/src/main/java/info/frostfs/sdk/tools/GrpcClient.java b/client/src/main/java/info/frostfs/sdk/tools/GrpcClient.java index 96ef9b2..747a249 100644 --- a/client/src/main/java/info/frostfs/sdk/tools/GrpcClient.java +++ b/client/src/main/java/info/frostfs/sdk/tools/GrpcClient.java @@ -3,6 +3,7 @@ package info.frostfs.sdk.tools; import io.grpc.Channel; import io.grpc.ChannelCredentials; import io.grpc.netty.NettyChannelBuilder; +import org.apache.commons.lang3.StringUtils; import java.net.URI; import java.net.URISyntaxException; @@ -11,17 +12,23 @@ import static java.util.Objects.isNull; public class GrpcClient { private static final String ERROR_INVALID_HOST_TEMPLATE = "Host %s has invalid format. Error: %s"; + private static final String ERROR_EMPTY_HOST_MESSAGE = "Host not provided"; private GrpcClient() { } - public static Channel initGrpcChannel(String host, ChannelCredentials creds) { + public static Channel initGrpcChannel(String host, ChannelCredentials credentials) { + if (StringUtils.isBlank(host)) { + throw new IllegalArgumentException(ERROR_EMPTY_HOST_MESSAGE); + } + try { URI uri = new URI(host); - var channelBuilder = isNull(creds) ? NettyChannelBuilder.forAddress(uri.getHost(), uri.getPort()) - : NettyChannelBuilder.forAddress(uri.getHost(), uri.getPort(), creds); + var channelBuilder = isNull(credentials) + ? NettyChannelBuilder.forAddress(uri.getHost(), uri.getPort()).usePlaintext() + : NettyChannelBuilder.forAddress(uri.getHost(), uri.getPort(), credentials); - return channelBuilder.usePlaintext().build(); + return channelBuilder.build(); } catch (URISyntaxException exp) { throw new IllegalArgumentException(String.format(ERROR_INVALID_HOST_TEMPLATE, host, exp.getMessage())); } diff --git a/client/src/main/java/info/frostfs/sdk/tools/MessageHelper.java b/client/src/main/java/info/frostfs/sdk/tools/MessageHelper.java deleted file mode 100644 index cb0005f..0000000 --- a/client/src/main/java/info/frostfs/sdk/tools/MessageHelper.java +++ /dev/null @@ -1,18 +0,0 @@ -package info.frostfs.sdk.tools; - -import com.google.protobuf.Message; -import com.google.protobuf.MessageOrBuilder; - -public class MessageHelper { - - private MessageHelper() { - } - - public static Message getField(MessageOrBuilder messageOrBuilder, String fieldName) { - return (Message) messageOrBuilder.getField(messageOrBuilder.getDescriptorForType().findFieldByName(fieldName)); - } - - public static void setField(Message.Builder builder, String fieldName, Object value) { - builder.setField(builder.getDescriptorForType().findFieldByName(fieldName), value); - } -} diff --git a/client/src/main/java/info/frostfs/sdk/tools/RequestConstructor.java b/client/src/main/java/info/frostfs/sdk/tools/RequestConstructor.java index 047d146..c50c532 100644 --- a/client/src/main/java/info/frostfs/sdk/tools/RequestConstructor.java +++ b/client/src/main/java/info/frostfs/sdk/tools/RequestConstructor.java @@ -1,63 +1,130 @@ package info.frostfs.sdk.tools; +import com.google.protobuf.ByteString; import com.google.protobuf.Message; import frostfs.session.Types; -import info.frostfs.sdk.dto.MetaHeader; +import info.frostfs.sdk.dto.response.MetaHeader; import info.frostfs.sdk.jdo.ECDsa; -import info.frostfs.sdk.mappers.MetaHeaderMapper; +import info.frostfs.sdk.mappers.response.MetaHeaderMapper; +import org.apache.commons.collections4.MapUtils; + +import java.util.Map; +import java.util.stream.Collectors; import static info.frostfs.sdk.constants.FieldConst.META_HEADER_FIELD_NAME; -import static info.frostfs.sdk.tools.MessageHelper.getField; -import static info.frostfs.sdk.tools.MessageHelper.setField; import static info.frostfs.sdk.tools.RequestSigner.signMessagePart; +import static info.frostfs.sdk.utils.MessageHelper.getField; +import static info.frostfs.sdk.utils.MessageHelper.setField; import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; public class RequestConstructor { + private static final String ERROR_MESSAGE = "The message does not contain a field " + META_HEADER_FIELD_NAME; private RequestConstructor() { } - public static void addDefaultMetaHeader(Message.Builder request) { - addMetaHeader(request, null); + public static void addMetaHeader(Message.Builder request) { + addMetaHeader(request, null, null); } - public static void addMetaHeader(Message.Builder request, Types.RequestMetaHeader metaHeader) { + public static void addMetaHeader(Message.Builder request, Map xHeaders) { + addMetaHeader(request, xHeaders, null); + } + + public static void addMetaHeader(Message.Builder request, + Map xHeaders, + Types.SessionToken sessionToken) { if (isNull(request)) { return; } - if (isNull(metaHeader) || metaHeader.getSerializedSize() == 0) { - metaHeader = MetaHeaderMapper.toGrpcMessage(new MetaHeader()); - setField(request, META_HEADER_FIELD_NAME, metaHeader); + if (isNull(request.getDescriptorForType().findFieldByName(META_HEADER_FIELD_NAME))) { + throw new IllegalArgumentException(ERROR_MESSAGE); } + + if (((Types.RequestMetaHeader) getField(request, META_HEADER_FIELD_NAME)).getSerializedSize() > 0) { + return; + } + + var metaHeader = MetaHeaderMapper.toGrpcMessageBuilder(new MetaHeader()); + + if (nonNull(sessionToken) && sessionToken.getSerializedSize() > 0) { + metaHeader.setSessionToken(sessionToken); + } + + if (MapUtils.isNotEmpty(xHeaders)) { + var grpcXHeaders = xHeaders.entrySet() + .stream() + .map(e -> Types.XHeader.newBuilder().setKey(e.getKey()).setValue(e.getValue()).build()) + .collect(Collectors.toList()); + + metaHeader.addAllXHeaders(grpcXHeaders); + } + + setField(request, META_HEADER_FIELD_NAME, metaHeader.build()); } - public static void addObjectSessionToken(Message.Builder request, - Types.SessionToken sessionToken, - frostfs.refs.Types.ContainerID cid, - frostfs.refs.Types.ObjectID oid, - Types.ObjectSessionContext.Verb verb, - ECDsa key) { - if (isNull(request) || isNull(sessionToken)) { - return; + public static Types.SessionToken createObjectTokenContext(Types.SessionToken sessionToken, + frostfs.refs.Types.Address address, + Types.ObjectSessionContext.Verb verb, + ECDsa key) { + if (isNull(sessionToken) || sessionToken.getBody().getObject().getTarget().getSerializedSize() > 0) { + return sessionToken; } - var header = (Types.RequestMetaHeader) getField(request, META_HEADER_FIELD_NAME); - if (header.getSessionToken().getSerializedSize() > 0) { - return; + var target = Types.ObjectSessionContext.Target.newBuilder() + .setContainer(address.getContainerId()); + + if (address.getObjectId().getSerializedSize() > 0) { + target.addObjects(address.getObjectId()); } var ctx = Types.ObjectSessionContext.newBuilder() - .setTarget(Types.ObjectSessionContext.Target.newBuilder().setContainer(cid).addObjects(oid).build()) + .setTarget(target.build()) .setVerb(verb) .build(); + var body = sessionToken.getBody().toBuilder() + .setObject(ctx) + .setSessionKey(ByteString.copyFrom(key.getPublicKeyByte())) + .build(); - var body = sessionToken.getBody().toBuilder().setObject(ctx).build(); - sessionToken = sessionToken.toBuilder() + return sessionToken.toBuilder() .setSignature(signMessagePart(key, body)) .setBody(body) .build(); + } - setField(request, META_HEADER_FIELD_NAME, header.toBuilder().setSessionToken(sessionToken).build()); + public static Types.SessionToken createContainerTokenContext(Types.SessionToken sessionToken, + frostfs.refs.Types.ContainerID containerId, + Types.ContainerSessionContext.Verb verb, + frostfs.refs.Types.OwnerID ownerId, + ECDsa key) { + if (isNull(sessionToken) || sessionToken.getBody().getContainer().getContainerId().getSerializedSize() > 0) { + return sessionToken; + } + + var containerSession = Types.ContainerSessionContext.newBuilder().setVerb(verb); + + if (isNull(containerId) || containerId.getSerializedSize() == 0) { + containerSession.setWildcard(true); + } else { + containerSession.setContainerId(containerId); + } + + var bodyBuilder = sessionToken.getBody().toBuilder() + .setContainer(containerSession) + .setSessionKey(ByteString.copyFrom(key.getPublicKeyByte())); + + if (nonNull(ownerId) && ownerId.getSerializedSize() > 0) { + bodyBuilder.setOwnerId(ownerId); + } + + var body = bodyBuilder.build(); + + return sessionToken.toBuilder() + .setSignature(signMessagePart(key, body)) + .setBody(body) + .build(); } } diff --git a/client/src/main/java/info/frostfs/sdk/tools/RequestSigner.java b/client/src/main/java/info/frostfs/sdk/tools/RequestSigner.java index 5cd178f..9bc3cc2 100644 --- a/client/src/main/java/info/frostfs/sdk/tools/RequestSigner.java +++ b/client/src/main/java/info/frostfs/sdk/tools/RequestSigner.java @@ -5,6 +5,7 @@ import com.google.protobuf.Message; import frostfs.session.Types; import info.frostfs.sdk.constants.CryptoConst; import info.frostfs.sdk.jdo.ECDsa; +import info.frostfs.sdk.utils.MessageHelper; import org.apache.commons.codec.digest.DigestUtils; import org.bouncycastle.asn1.sec.SECNamedCurves; import org.bouncycastle.asn1.sec.SECObjectIdentifiers; @@ -92,10 +93,10 @@ public class RequestSigner { } public static void sign(Message.Builder request, ECDsa key) { - var meta = MessageHelper.getField(request, META_HEADER_FIELD_NAME); - var body = MessageHelper.getField(request, BODY_FIELD_NAME); - var verify = MessageHelper.getField(request, VERIFY_HEADER_FIELD_NAME); - var verifyOrigin = MessageHelper.getField(verify, ORIGIN_FIELD_NAME); + var meta = (Message) MessageHelper.getField(request, META_HEADER_FIELD_NAME); + var body = (Message) MessageHelper.getField(request, BODY_FIELD_NAME); + var verify = (Message) MessageHelper.getField(request, VERIFY_HEADER_FIELD_NAME); + var verifyOrigin = (Message) MessageHelper.getField(verify, ORIGIN_FIELD_NAME); Message.Builder verifyBuilder; if (verify instanceof Types.RequestVerificationHeader) { diff --git a/client/src/main/java/info/frostfs/sdk/tools/Verifier.java b/client/src/main/java/info/frostfs/sdk/tools/Verifier.java index f0cd408..3af7898 100644 --- a/client/src/main/java/info/frostfs/sdk/tools/Verifier.java +++ b/client/src/main/java/info/frostfs/sdk/tools/Verifier.java @@ -3,7 +3,9 @@ package info.frostfs.sdk.tools; import com.google.protobuf.Message; import frostfs.session.Types; import info.frostfs.sdk.constants.CryptoConst; -import info.frostfs.sdk.mappers.StatusMapper; +import info.frostfs.sdk.exceptions.ResponseException; +import info.frostfs.sdk.mappers.response.ResponseStatusMapper; +import info.frostfs.sdk.utils.MessageHelper; import org.apache.commons.codec.digest.DigestUtils; import org.bouncycastle.asn1.sec.SECNamedCurves; import org.bouncycastle.asn1.sec.SECObjectIdentifiers; @@ -73,14 +75,14 @@ public class Verifier { } var metaHeader = (Types.ResponseMetaHeader) MessageHelper.getField(response, META_HEADER_FIELD_NAME); - var status = StatusMapper.toModel(metaHeader.getStatus()); + var status = ResponseStatusMapper.toModel(metaHeader.getStatus()); if (!status.isSuccess()) { - throw new IllegalArgumentException(status.toString()); + throw new ResponseException(status); } } public static boolean verify(Message response) { - var body = MessageHelper.getField(response, BODY_FIELD_NAME); + var body = (Message) MessageHelper.getField(response, BODY_FIELD_NAME); var metaHeader = (Types.ResponseMetaHeader) MessageHelper.getField(response, META_HEADER_FIELD_NAME); var verifyHeader = (Types.ResponseVerificationHeader) MessageHelper.getField(response, VERIFY_HEADER_FIELD_NAME); diff --git a/client/src/main/java/info/frostfs/sdk/utils/MessageHelper.java b/client/src/main/java/info/frostfs/sdk/utils/MessageHelper.java new file mode 100644 index 0000000..703db82 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/utils/MessageHelper.java @@ -0,0 +1,30 @@ +package info.frostfs.sdk.utils; + +import com.google.protobuf.Message; +import com.google.protobuf.MessageOrBuilder; +import org.apache.commons.lang3.StringUtils; + +import static java.util.Objects.isNull; + +public class MessageHelper { + private static final String ERROR_MESSAGE = "One of the input parameters is null"; + + private MessageHelper() { + } + + public static Object getField(MessageOrBuilder messageOrBuilder, String fieldName) { + if (isNull(messageOrBuilder) || StringUtils.isBlank(fieldName)) { + throw new IllegalArgumentException(ERROR_MESSAGE); + } + + return messageOrBuilder.getField(messageOrBuilder.getDescriptorForType().findFieldByName(fieldName)); + } + + public static void setField(Message.Builder builder, String fieldName, Object value) { + if (isNull(builder) || StringUtils.isBlank(fieldName) || isNull(value)) { + throw new IllegalArgumentException(ERROR_MESSAGE); + } + + builder.setField(builder.getDescriptorForType().findFieldByName(fieldName), value); + } +} diff --git a/client/src/main/java/info/frostfs/sdk/utils/ValidatorUtils.java b/client/src/main/java/info/frostfs/sdk/utils/ValidatorUtils.java new file mode 100644 index 0000000..20d6b15 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/utils/ValidatorUtils.java @@ -0,0 +1,42 @@ +package info.frostfs.sdk.utils; + +import org.apache.commons.collections4.CollectionUtils; + +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.ValidationException; +import javax.validation.Validator; +import java.util.Set; + +import static java.util.Objects.isNull; + +public class ValidatorUtils { + private static final String OBJECT_IS_NULL = "object is null"; + private static final String ERROR_PROPERTY_TEMPLATE = "property %s with value %s %s"; + + private static final Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); + + + public static void validate(T object, Class... classes) { + if (isNull(object)) { + throw new ValidationException(OBJECT_IS_NULL); + } + + Set> violations = validator.validate(object, classes); + + if (CollectionUtils.isEmpty(violations)) { + return; + } + + String[] errors = (String[]) violations.stream() + .map(violation -> String.format( + ERROR_PROPERTY_TEMPLATE, + violation.getPropertyPath().toString(), + violation.getInvalidValue(), + violation.getMessage() + )) + .toArray(); + + throw new ValidationException(String.join(",", errors)); + } +} diff --git a/client/src/main/java/info/frostfs/sdk/utils/WaitUtil.java b/client/src/main/java/info/frostfs/sdk/utils/WaitUtil.java new file mode 100644 index 0000000..27e785d --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/utils/WaitUtil.java @@ -0,0 +1,12 @@ +package info.frostfs.sdk.utils; + +public class WaitUtil { + + public static void sleep(long ms) { + try { + Thread.sleep(ms); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } + } +} diff --git a/cryptography/src/main/java/info/frostfs/sdk/ArrayHelper.java b/cryptography/src/main/java/info/frostfs/sdk/ArrayHelper.java index b21a3f2..6ccfe65 100644 --- a/cryptography/src/main/java/info/frostfs/sdk/ArrayHelper.java +++ b/cryptography/src/main/java/info/frostfs/sdk/ArrayHelper.java @@ -13,10 +13,10 @@ public class ArrayHelper { throw new IllegalArgumentException(ERROR_MESSAGE); } - byte[] result = new byte[startArray.length + endArray.length]; + byte[] joinedArray = new byte[startArray.length + endArray.length]; - System.arraycopy(startArray, 0, result, 0, startArray.length); - System.arraycopy(endArray, 0, result, startArray.length, endArray.length); - return result; + System.arraycopy(startArray, 0, joinedArray, 0, startArray.length); + System.arraycopy(endArray, 0, joinedArray, startArray.length, endArray.length); + return joinedArray; } } diff --git a/models/src/main/java/info/frostfs/sdk/constants/FilterConst.java b/models/src/main/java/info/frostfs/sdk/constants/FilterConst.java new file mode 100644 index 0000000..5340057 --- /dev/null +++ b/models/src/main/java/info/frostfs/sdk/constants/FilterConst.java @@ -0,0 +1,49 @@ +package info.frostfs.sdk.constants; + +public class FilterConst { + + // HEADER_PREFIX is a prefix of key to object header value or property. + public static final String HEADER_PREFIX = "$Object:"; + + // FILTER_HEADER_VERSION is a filter key to "version" field of the object header. + public static final String FILTER_HEADER_VERSION = HEADER_PREFIX + "version"; + + // FILTER_HEADER_OBJECT_ID is a filter key to "object_id" field of the object. + public static final String FILTER_HEADER_OBJECT_ID = HEADER_PREFIX + "objectID"; + + // FILTER_HEADER_CONTAINER_ID is a filter key to "container_id" field of the object header. + public static final String FILTER_HEADER_CONTAINER_ID = HEADER_PREFIX + "containerID"; + + // FILTER_HEADER_OWNER_ID is a filter key to "owner_id" field of the object header. + public static final String FILTER_HEADER_OWNER_ID = HEADER_PREFIX + "ownerID"; + + // FILTER_HEADER_CREATION_EPOCH is a filter key to "creation_epoch" field of the object header. + public static final String FILTER_HEADER_CREATION_EPOCH = HEADER_PREFIX + "creationEpoch"; + + // FILTER_HEADER_PAYLOAD_LENGTH is a filter key to "payload_length" field of the object header. + public static final String FILTER_HEADER_PAYLOAD_LENGTH = HEADER_PREFIX + "payloadLength"; + + // FILTER_HEADER_PAYLOAD_HASH is a filter key to "payload_hash" field of the object header. + public static final String FILTER_HEADER_PAYLOAD_HASH = HEADER_PREFIX + "payloadHash"; + + // FILTER_HEADER_OBJECT_TYPE is a filter key to "object_type" field of the object header. + public static final String FILTER_HEADER_OBJECT_TYPE = HEADER_PREFIX + "objectType"; + + // FILTER_HEADER_HOMOMORPHIC_HASH is a filter key to "homomorphic_hash" field of the object header. + public static final String FILTER_HEADER_HOMOMORPHIC_HASH = HEADER_PREFIX + "homomorphicHash"; + + // FILTER_HEADER_PARENT is a filter key to "split.parent" field of the object header. + public static final String FILTER_HEADER_PARENT = HEADER_PREFIX + "split.parent"; + + // FILTER_HEADER_SPLIT_ID is a filter key to "split.splitID" field of the object header. + public static final String FILTER_HEADER_SPLIT_ID = HEADER_PREFIX + "split.splitID"; + + // FILTER_HEADER_EC_PARENT is a filter key to "ec.parent" field of the object header. + public static final String FILTER_HEADER_EC_PARENT = HEADER_PREFIX + "ec.parent"; + + // FILTER_HEADER_ROOT is a filter key to check if regular object is on top of split hierarchy. + public static final String FILTER_HEADER_ROOT = HEADER_PREFIX + "ROOT"; + + // FILTER_HEADER_PHY is a filter key to check if an object physically stored on a node. + public static final String FILTER_HEADER_PHY = HEADER_PREFIX + "PHY"; +} diff --git a/models/src/main/java/info/frostfs/sdk/dto/CheckSum.java b/models/src/main/java/info/frostfs/sdk/dto/CheckSum.java new file mode 100644 index 0000000..ad298c4 --- /dev/null +++ b/models/src/main/java/info/frostfs/sdk/dto/CheckSum.java @@ -0,0 +1,25 @@ +package info.frostfs.sdk.dto; + +import info.frostfs.sdk.Helper; + +public class CheckSum { + // type is always Sha256 + public byte[] hash; + + public CheckSum(byte[] content) { + this.hash = Helper.getSha256(content); + } + + public byte[] getHash() { + return hash; + } + + public void setHash(byte[] hash) { + this.hash = hash; + } + + @Override + public String toString() { + return Helper.getHexString(hash); + } +} diff --git a/models/src/main/java/info/frostfs/sdk/dto/SessionToken.java b/models/src/main/java/info/frostfs/sdk/dto/SessionToken.java deleted file mode 100644 index 2f4055b..0000000 --- a/models/src/main/java/info/frostfs/sdk/dto/SessionToken.java +++ /dev/null @@ -1,19 +0,0 @@ -package info.frostfs.sdk.dto; - -public class SessionToken { - private final byte[] id; - private final byte[] sessionKey; - - public SessionToken(byte[] id, byte[] sessionKey) { - this.id = id; - this.sessionKey = sessionKey; - } - - public byte[] getId() { - return id; - } - - public byte[] getSessionKey() { - return sessionKey; - } -} diff --git a/models/src/main/java/info/frostfs/sdk/dto/container/Container.java b/models/src/main/java/info/frostfs/sdk/dto/container/Container.java index e07c28a..174d487 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/container/Container.java +++ b/models/src/main/java/info/frostfs/sdk/dto/container/Container.java @@ -1,7 +1,7 @@ package info.frostfs.sdk.dto.container; -import info.frostfs.sdk.dto.Version; import info.frostfs.sdk.dto.netmap.PlacementPolicy; +import info.frostfs.sdk.dto.netmap.Version; import info.frostfs.sdk.enums.BasicAcl; import java.util.UUID; diff --git a/models/src/main/java/info/frostfs/sdk/dto/netmap/NetmapSnapshot.java b/models/src/main/java/info/frostfs/sdk/dto/netmap/NetmapSnapshot.java index 9c2ef2c..69670bb 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/netmap/NetmapSnapshot.java +++ b/models/src/main/java/info/frostfs/sdk/dto/netmap/NetmapSnapshot.java @@ -1,5 +1,6 @@ package info.frostfs.sdk.dto.netmap; +import java.util.Collections; import java.util.List; public class NetmapSnapshot { @@ -8,7 +9,7 @@ public class NetmapSnapshot { public NetmapSnapshot(Long epoch, List nodeInfoCollection) { this.epoch = epoch; - this.nodeInfoCollection = nodeInfoCollection; + this.nodeInfoCollection = Collections.unmodifiableList(nodeInfoCollection); } public Long getEpoch() { diff --git a/models/src/main/java/info/frostfs/sdk/dto/netmap/NodeInfo.java b/models/src/main/java/info/frostfs/sdk/dto/netmap/NodeInfo.java index 2be1b1b..75c2484 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/netmap/NodeInfo.java +++ b/models/src/main/java/info/frostfs/sdk/dto/netmap/NodeInfo.java @@ -1,8 +1,8 @@ package info.frostfs.sdk.dto.netmap; -import info.frostfs.sdk.dto.Version; import info.frostfs.sdk.enums.NodeState; +import java.util.Collections; import java.util.List; import java.util.Map; @@ -17,8 +17,8 @@ public class NodeInfo { Map attributes, byte[] publicKey) { this.state = state; this.version = version; - this.addresses = addresses; - this.attributes = attributes; + this.addresses = Collections.unmodifiableList(addresses); + this.attributes = Collections.unmodifiableMap(attributes); this.publicKey = publicKey; } diff --git a/models/src/main/java/info/frostfs/sdk/dto/Version.java b/models/src/main/java/info/frostfs/sdk/dto/netmap/Version.java similarity index 95% rename from models/src/main/java/info/frostfs/sdk/dto/Version.java rename to models/src/main/java/info/frostfs/sdk/dto/netmap/Version.java index 0222922..7f9b54c 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/Version.java +++ b/models/src/main/java/info/frostfs/sdk/dto/netmap/Version.java @@ -1,4 +1,4 @@ -package info.frostfs.sdk.dto; +package info.frostfs.sdk.dto.netmap; import static info.frostfs.sdk.constants.AppConst.DEFAULT_MAJOR_VERSION; import static info.frostfs.sdk.constants.AppConst.DEFAULT_MINOR_VERSION; diff --git a/models/src/main/java/info/frostfs/sdk/dto/object/LinkObject.java b/models/src/main/java/info/frostfs/sdk/dto/object/LinkObject.java index d7a7091..a2ffb6f 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/object/LinkObject.java +++ b/models/src/main/java/info/frostfs/sdk/dto/object/LinkObject.java @@ -1,7 +1,5 @@ package info.frostfs.sdk.dto.object; -import info.frostfs.sdk.dto.Split; -import info.frostfs.sdk.dto.SplitId; import info.frostfs.sdk.dto.container.ContainerId; import org.apache.commons.collections4.CollectionUtils; @@ -9,10 +7,10 @@ import java.util.List; public class LinkObject extends ObjectFrostFS { - public LinkObject(ContainerId cid, SplitId splitId, LargeObject largeObject) { + public LinkObject(ContainerId cid, SplitId splitId, ObjectHeader largeObjectHeader) { super(cid, new byte[]{}); var split = new Split(splitId); - split.setParentHeader(largeObject.getHeader()); + split.setParentHeader(largeObjectHeader); this.getHeader().setSplit(split); } diff --git a/models/src/main/java/info/frostfs/sdk/dto/object/ObjectFilter.java b/models/src/main/java/info/frostfs/sdk/dto/object/ObjectFilter.java index 1443ee2..575f5e1 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/object/ObjectFilter.java +++ b/models/src/main/java/info/frostfs/sdk/dto/object/ObjectFilter.java @@ -1,39 +1,24 @@ package info.frostfs.sdk.dto.object; -import info.frostfs.sdk.dto.OwnerId; -import info.frostfs.sdk.dto.Version; +import info.frostfs.sdk.constants.FieldConst; +import info.frostfs.sdk.constants.FilterConst; +import info.frostfs.sdk.dto.CheckSum; +import info.frostfs.sdk.dto.container.ContainerId; +import info.frostfs.sdk.dto.netmap.Version; import info.frostfs.sdk.enums.ObjectMatchType; -public class ObjectFilter { - private static final String HEADER_PREFIX = "$Object:"; - +public abstract class ObjectFilter { private ObjectMatchType matchType; private String key; - private String value; + private T value; - public ObjectFilter(ObjectMatchType matchType, String key, String value) { + public ObjectFilter(ObjectMatchType matchType, String key, T value) { this.matchType = matchType; this.key = key; this.value = value; } - public static ObjectFilter ObjectIdFilter(ObjectMatchType matchType, ObjectId objectId) { - return new ObjectFilter(matchType, HEADER_PREFIX + "objectID", objectId.getValue()); - } - - public static ObjectFilter OwnerFilter(ObjectMatchType matchType, OwnerId ownerId) { - return new ObjectFilter(matchType, HEADER_PREFIX + "ownerID", ownerId.getValue()); - } - - public static ObjectFilter RootFilter() { - return new ObjectFilter(ObjectMatchType.UNSPECIFIED, HEADER_PREFIX + "ROOT", ""); - } - - public static ObjectFilter VersionFilter(ObjectMatchType matchType, Version version) { - return new ObjectFilter(matchType, HEADER_PREFIX + "version", version.toString()); - } - public ObjectMatchType getMatchType() { return matchType; } @@ -50,11 +35,94 @@ public class ObjectFilter { this.key = key; } - public String getValue() { + public T getValue() { return value; } - public void setValue(String value) { + public void setValue(T value) { this.value = value; } + + public String getSerializedValue() { + return value.toString(); + } + + public static class FilterByAttribute extends ObjectFilter { + public FilterByAttribute(ObjectMatchType matchType, String key, String value) { + super(matchType, key, value); + } + } + + public static class FilterByObjectId extends ObjectFilter { + public FilterByObjectId(ObjectMatchType matchType, ObjectId value) { + super(matchType, FilterConst.FILTER_HEADER_OBJECT_ID, value); + } + } + + public static class FilterByOwnerId extends ObjectFilter { + public FilterByOwnerId(ObjectMatchType matchType, OwnerId value) { + super(matchType, FilterConst.FILTER_HEADER_OWNER_ID, value); + } + } + + public static class FilterByVersion extends ObjectFilter { + public FilterByVersion(ObjectMatchType matchType, Version value) { + super(matchType, FilterConst.FILTER_HEADER_VERSION, value); + } + } + + public static class FilterByContainerId extends ObjectFilter { + public FilterByContainerId(ObjectMatchType matchType, ContainerId value) { + super(matchType, FilterConst.FILTER_HEADER_CONTAINER_ID, value); + } + } + + public static class FilterByEpoch extends ObjectFilter { + public FilterByEpoch(ObjectMatchType matchType, long value) { + super(matchType, FilterConst.FILTER_HEADER_CREATION_EPOCH, value); + } + } + + public static class FilterByPayloadLength extends ObjectFilter { + public FilterByPayloadLength(ObjectMatchType matchType, long value) { + super(matchType, FilterConst.FILTER_HEADER_PAYLOAD_LENGTH, value); + } + } + + public static class FilterByPayloadHash extends ObjectFilter { + public FilterByPayloadHash(ObjectMatchType matchType, CheckSum value) { + super(matchType, FilterConst.FILTER_HEADER_PAYLOAD_HASH, value); + } + } + + public static class FilterByParent extends ObjectFilter { + public FilterByParent(ObjectMatchType matchType, ObjectId value) { + super(matchType, FilterConst.FILTER_HEADER_PARENT, value); + } + } + + public static class FilterBySplitId extends ObjectFilter { + public FilterBySplitId(ObjectMatchType matchType, SplitId value) { + super(matchType, FilterConst.FILTER_HEADER_SPLIT_ID, value); + } + } + + + public static class FilterByECParent extends ObjectFilter { + public FilterByECParent(ObjectMatchType matchType, ObjectId value) { + super(matchType, FilterConst.FILTER_HEADER_EC_PARENT, value); + } + } + + public static class FilterByRootObject extends ObjectFilter { + public FilterByRootObject() { + super(ObjectMatchType.UNSPECIFIED, FilterConst.FILTER_HEADER_ROOT, FieldConst.EMPTY_STRING); + } + } + + public static class FilterByPhysicallyStored extends ObjectFilter { + public FilterByPhysicallyStored() { + super(ObjectMatchType.UNSPECIFIED, FilterConst.FILTER_HEADER_PHY, FieldConst.EMPTY_STRING); + } + } } diff --git a/models/src/main/java/info/frostfs/sdk/dto/object/ObjectFrostFS.java b/models/src/main/java/info/frostfs/sdk/dto/object/ObjectFrostFS.java index af5f463..53195bd 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/object/ObjectFrostFS.java +++ b/models/src/main/java/info/frostfs/sdk/dto/object/ObjectFrostFS.java @@ -1,10 +1,8 @@ package info.frostfs.sdk.dto.object; -import info.frostfs.sdk.dto.Split; import info.frostfs.sdk.dto.container.ContainerId; import info.frostfs.sdk.enums.ObjectType; -import java.util.ArrayList; import java.util.List; import static java.util.Objects.isNull; @@ -13,6 +11,7 @@ public class ObjectFrostFS { private final ObjectHeader header; private ObjectId objectId; private byte[] payload; + private ObjectReader objectReader; public ObjectFrostFS(ObjectHeader header, ObjectId objectId, byte[] payload) { if (isNull(header)) { @@ -26,12 +25,20 @@ public class ObjectFrostFS { public ObjectFrostFS(ContainerId containerId, byte[] payload) { this.payload = payload; - this.header = new ObjectHeader(containerId, new ArrayList<>()); + this.header = new ObjectHeader(containerId); } public ObjectFrostFS(ContainerId containerId, byte[] payload, ObjectType objectType) { this.payload = payload; - this.header = new ObjectHeader(containerId, objectType, new ArrayList<>()); + this.header = new ObjectHeader(containerId, objectType); + } + + public ObjectReader getObjectReader() { + return objectReader; + } + + public void setObjectReader(ObjectReader objectReader) { + this.objectReader = objectReader; } public ObjectHeader getHeader() { diff --git a/models/src/main/java/info/frostfs/sdk/dto/object/ObjectHeader.java b/models/src/main/java/info/frostfs/sdk/dto/object/ObjectHeader.java index 9cc1f47..3878736 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/object/ObjectHeader.java +++ b/models/src/main/java/info/frostfs/sdk/dto/object/ObjectHeader.java @@ -1,12 +1,12 @@ package info.frostfs.sdk.dto.object; -import info.frostfs.sdk.dto.OwnerId; -import info.frostfs.sdk.dto.Split; -import info.frostfs.sdk.dto.Version; import info.frostfs.sdk.dto.container.ContainerId; +import info.frostfs.sdk.dto.netmap.Version; import info.frostfs.sdk.enums.ObjectType; +import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; import static java.util.Objects.isNull; @@ -33,24 +33,18 @@ public class ObjectHeader { this.version = version; } - public ObjectHeader(ContainerId containerId, ObjectType objectType, List attributes) { - if (isNull(containerId) || isNull(objectType)) { - throw new IllegalArgumentException("ContainerId or objectType is not present"); - } - - this.attributes = attributes; - this.containerId = containerId; - this.objectType = objectType; + public ObjectHeader(ContainerId containerId, ObjectType objectType, ObjectAttribute... attributes) { + this( + containerId, + objectType, + Arrays.stream(attributes).collect(Collectors.toList()), + 0L, + null + ); } - public ObjectHeader(ContainerId containerId, List attributes) { - if (isNull(containerId)) { - throw new IllegalArgumentException("ContainerId is not present"); - } - - this.attributes = attributes; - this.containerId = containerId; - this.objectType = ObjectType.REGULAR; + public ObjectHeader(ContainerId containerId, ObjectAttribute... attributes) { + this(containerId, ObjectType.REGULAR, attributes); } public OwnerId getOwnerId() { diff --git a/models/src/main/java/info/frostfs/sdk/dto/object/ObjectReader.java b/models/src/main/java/info/frostfs/sdk/dto/object/ObjectReader.java new file mode 100644 index 0000000..1647c3c --- /dev/null +++ b/models/src/main/java/info/frostfs/sdk/dto/object/ObjectReader.java @@ -0,0 +1,5 @@ +package info.frostfs.sdk.dto.object; + +public interface ObjectReader { + byte[] readChunk(); +} diff --git a/models/src/main/java/info/frostfs/sdk/dto/OwnerId.java b/models/src/main/java/info/frostfs/sdk/dto/object/OwnerId.java similarity index 85% rename from models/src/main/java/info/frostfs/sdk/dto/OwnerId.java rename to models/src/main/java/info/frostfs/sdk/dto/object/OwnerId.java index 1603e21..e15a5eb 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/OwnerId.java +++ b/models/src/main/java/info/frostfs/sdk/dto/object/OwnerId.java @@ -1,4 +1,4 @@ -package info.frostfs.sdk.dto; +package info.frostfs.sdk.dto.object; import info.frostfs.sdk.Base58; @@ -27,4 +27,9 @@ public class OwnerId { public byte[] toHash() { return Base58.decode(value); } + + @Override + public String toString() { + return value; + } } diff --git a/models/src/main/java/info/frostfs/sdk/dto/Split.java b/models/src/main/java/info/frostfs/sdk/dto/object/Split.java similarity index 78% rename from models/src/main/java/info/frostfs/sdk/dto/Split.java rename to models/src/main/java/info/frostfs/sdk/dto/object/Split.java index 642ed58..93836d2 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/Split.java +++ b/models/src/main/java/info/frostfs/sdk/dto/object/Split.java @@ -1,7 +1,7 @@ -package info.frostfs.sdk.dto; +package info.frostfs.sdk.dto.object; -import info.frostfs.sdk.dto.object.ObjectHeader; -import info.frostfs.sdk.dto.object.ObjectId; +import frostfs.refs.Types; +import info.frostfs.sdk.dto.response.Signature; import java.util.ArrayList; import java.util.List; @@ -15,6 +15,7 @@ public class Split { private ObjectId previous; private Signature parentSignature; private ObjectHeader parentHeader; + private Types.Signature parentSignatureGrpc; public Split() { this(new SplitId()); @@ -68,4 +69,12 @@ public class Split { public List getChildren() { return children; } + + public Types.Signature getParentSignatureGrpc() { + return parentSignatureGrpc; + } + + public void setParentSignatureGrpc(Types.Signature parentSignatureGrpc) { + this.parentSignatureGrpc = parentSignatureGrpc; + } } diff --git a/models/src/main/java/info/frostfs/sdk/dto/SplitId.java b/models/src/main/java/info/frostfs/sdk/dto/object/SplitId.java similarity index 83% rename from models/src/main/java/info/frostfs/sdk/dto/SplitId.java rename to models/src/main/java/info/frostfs/sdk/dto/object/SplitId.java index 29ed2d0..994d184 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/SplitId.java +++ b/models/src/main/java/info/frostfs/sdk/dto/object/SplitId.java @@ -1,4 +1,4 @@ -package info.frostfs.sdk.dto; +package info.frostfs.sdk.dto.object; import java.util.UUID; @@ -21,8 +21,8 @@ public class SplitId { this.id = asUuid(binary); } - public SplitId(String str) { - this.id = UUID.fromString(str); + public SplitId(String string) { + this.id = UUID.fromString(string); } @Override diff --git a/models/src/main/java/info/frostfs/sdk/dto/MetaHeader.java b/models/src/main/java/info/frostfs/sdk/dto/response/MetaHeader.java similarity index 94% rename from models/src/main/java/info/frostfs/sdk/dto/MetaHeader.java rename to models/src/main/java/info/frostfs/sdk/dto/response/MetaHeader.java index 9d6735e..9a1faff 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/MetaHeader.java +++ b/models/src/main/java/info/frostfs/sdk/dto/response/MetaHeader.java @@ -1,4 +1,6 @@ -package info.frostfs.sdk.dto; +package info.frostfs.sdk.dto.response; + +import info.frostfs.sdk.dto.netmap.Version; import static info.frostfs.sdk.constants.AppConst.DEFAULT_MAJOR_VERSION; import static info.frostfs.sdk.constants.AppConst.DEFAULT_MINOR_VERSION; diff --git a/models/src/main/java/info/frostfs/sdk/dto/Status.java b/models/src/main/java/info/frostfs/sdk/dto/response/ResponseStatus.java similarity index 83% rename from models/src/main/java/info/frostfs/sdk/dto/Status.java rename to models/src/main/java/info/frostfs/sdk/dto/response/ResponseStatus.java index e1075b5..66e7031 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/Status.java +++ b/models/src/main/java/info/frostfs/sdk/dto/response/ResponseStatus.java @@ -1,20 +1,20 @@ -package info.frostfs.sdk.dto; +package info.frostfs.sdk.dto.response; import info.frostfs.sdk.enums.StatusCode; import static info.frostfs.sdk.constants.FieldConst.EMPTY_STRING; import static java.util.Objects.isNull; -public class Status { +public class ResponseStatus { private StatusCode code; private String message; - public Status(StatusCode code, String message) { + public ResponseStatus(StatusCode code, String message) { this.code = code; this.message = isNull(message) ? EMPTY_STRING : message; } - public Status(StatusCode code) { + public ResponseStatus(StatusCode code) { this.code = code; this.message = EMPTY_STRING; } diff --git a/models/src/main/java/info/frostfs/sdk/dto/Signature.java b/models/src/main/java/info/frostfs/sdk/dto/response/Signature.java similarity index 93% rename from models/src/main/java/info/frostfs/sdk/dto/Signature.java rename to models/src/main/java/info/frostfs/sdk/dto/response/Signature.java index 5b2b85d..65d6ce8 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/Signature.java +++ b/models/src/main/java/info/frostfs/sdk/dto/response/Signature.java @@ -1,4 +1,4 @@ -package info.frostfs.sdk.dto; +package info.frostfs.sdk.dto.response; import info.frostfs.sdk.enums.SignatureScheme; diff --git a/models/src/main/java/info/frostfs/sdk/dto/session/SessionToken.java b/models/src/main/java/info/frostfs/sdk/dto/session/SessionToken.java new file mode 100644 index 0000000..2c4ae83 --- /dev/null +++ b/models/src/main/java/info/frostfs/sdk/dto/session/SessionToken.java @@ -0,0 +1,13 @@ +package info.frostfs.sdk.dto.session; + +public class SessionToken { + private final byte[] token; + + public SessionToken(byte[] token) { + this.token = token; + } + + public byte[] getToken() { + return token; + } +} diff --git a/models/src/main/java/info/frostfs/sdk/mappers/MetaHeaderMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/MetaHeaderMapper.java deleted file mode 100644 index 0b90453..0000000 --- a/models/src/main/java/info/frostfs/sdk/mappers/MetaHeaderMapper.java +++ /dev/null @@ -1,24 +0,0 @@ -package info.frostfs.sdk.mappers; - -import frostfs.session.Types; -import info.frostfs.sdk.dto.MetaHeader; - -import static java.util.Objects.isNull; - -public class MetaHeaderMapper { - - private MetaHeaderMapper() { - } - - public static Types.RequestMetaHeader toGrpcMessage(MetaHeader metaHeader) { - if (isNull(metaHeader)) { - return null; - } - - return Types.RequestMetaHeader.newBuilder() - .setVersion(VersionMapper.toGrpcMessage(metaHeader.getVersion())) - .setEpoch(metaHeader.getEpoch()) - .setTtl(metaHeader.getTtl()) - .build(); - } -} diff --git a/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerMapper.java index c02bfcb..e874859 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerMapper.java @@ -4,8 +4,8 @@ import com.google.protobuf.ByteString; import frostfs.container.Types; import info.frostfs.sdk.dto.container.Container; import info.frostfs.sdk.enums.BasicAcl; -import info.frostfs.sdk.mappers.VersionMapper; import info.frostfs.sdk.mappers.netmap.PlacementPolicyMapper; +import info.frostfs.sdk.mappers.netmap.VersionMapper; import static info.frostfs.sdk.UuidExtension.asBytes; import static info.frostfs.sdk.UuidExtension.asUuid; diff --git a/models/src/main/java/info/frostfs/sdk/mappers/netmap/NodeInfoMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/netmap/NodeInfoMapper.java index a5791b3..97fa499 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/netmap/NodeInfoMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/netmap/NodeInfoMapper.java @@ -5,7 +5,6 @@ import frostfs.netmap.Types; import frostfs.netmap.Types.NodeInfo.Attribute; import info.frostfs.sdk.dto.netmap.NodeInfo; import info.frostfs.sdk.enums.NodeState; -import info.frostfs.sdk.mappers.VersionMapper; import java.util.stream.Collectors; diff --git a/models/src/main/java/info/frostfs/sdk/mappers/VersionMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/netmap/VersionMapper.java similarity index 88% rename from models/src/main/java/info/frostfs/sdk/mappers/VersionMapper.java rename to models/src/main/java/info/frostfs/sdk/mappers/netmap/VersionMapper.java index f12cb73..386cb78 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/VersionMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/netmap/VersionMapper.java @@ -1,7 +1,7 @@ -package info.frostfs.sdk.mappers; +package info.frostfs.sdk.mappers.netmap; import frostfs.refs.Types; -import info.frostfs.sdk.dto.Version; +import info.frostfs.sdk.dto.netmap.Version; import static java.util.Objects.isNull; diff --git a/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectFilterMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectFilterMapper.java index 874e9b1..8ebf944 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectFilterMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectFilterMapper.java @@ -12,7 +12,7 @@ public class ObjectFilterMapper { private ObjectFilterMapper() { } - public static Service.SearchRequest.Body.Filter toGrpcMessage(ObjectFilter filter) { + public static Service.SearchRequest.Body.Filter toGrpcMessage(ObjectFilter filter) { if (isNull(filter)) { return null; } @@ -27,7 +27,7 @@ public class ObjectFilterMapper { return Service.SearchRequest.Body.Filter.newBuilder() .setMatchType(objectMatchType) .setKey(filter.getKey()) - .setValue(filter.getValue()) + .setValue(filter.getSerializedValue()) .build(); } } diff --git a/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectHeaderMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectHeaderMapper.java index af8b898..93c432c 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectHeaderMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectHeaderMapper.java @@ -1,27 +1,30 @@ package info.frostfs.sdk.mappers.object; +import com.google.protobuf.ByteString; import frostfs.object.Types; import info.frostfs.sdk.dto.container.ContainerId; import info.frostfs.sdk.dto.object.ObjectAttribute; import info.frostfs.sdk.dto.object.ObjectHeader; import info.frostfs.sdk.enums.ObjectType; -import info.frostfs.sdk.mappers.VersionMapper; import info.frostfs.sdk.mappers.container.ContainerIdMapper; +import info.frostfs.sdk.mappers.netmap.VersionMapper; import org.apache.commons.collections4.ListUtils; import java.util.stream.Collectors; import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; public class ObjectHeaderMapper { private static final String ERROR_UNKNOWN_VALUE_TEMPLATE = "Unknown ObjectType. Value: %s."; + private static final String ERROR_OBJECT_HEADER_MISSING_ERROR = "ObjectHeader is not present"; private ObjectHeaderMapper() { } public static Types.Header toGrpcMessage(ObjectHeader header) { if (isNull(header)) { - return null; + throw new IllegalArgumentException(ERROR_OBJECT_HEADER_MISSING_ERROR); } var objectType = Types.ObjectType.forNumber(header.getObjectType().value); @@ -32,13 +35,23 @@ public class ObjectHeaderMapper { } var head = Types.Header.newBuilder() + .setOwnerId(OwnerIdMapper.toGrpcMessage(header.getOwnerId())) + .setVersion(VersionMapper.toGrpcMessage(header.getVersion())) .setContainerId(ContainerIdMapper.toGrpcMessage(header.getContainerId())) - .setObjectType(objectType); + .setObjectType(objectType) + .setPayloadLength(header.getPayloadLength()); for (ObjectAttribute objectAttribute : header.getAttributes()) { head.addAttributes(ObjectAttributeMapper.toGrpcMessage(objectAttribute)); } + if (nonNull(header.getSplit())) { + var grpcSplit = Types.Header.Split.newBuilder() + .setSplitId(ByteString.copyFrom(header.getSplit().getSplitId().toBinary())) + .build(); + head.setSplit(grpcSplit); + } + return head.build(); } diff --git a/models/src/main/java/info/frostfs/sdk/mappers/OwnerIdMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/object/OwnerIdMapper.java similarity index 84% rename from models/src/main/java/info/frostfs/sdk/mappers/OwnerIdMapper.java rename to models/src/main/java/info/frostfs/sdk/mappers/object/OwnerIdMapper.java index a0a6b64..905f879 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/OwnerIdMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/object/OwnerIdMapper.java @@ -1,8 +1,8 @@ -package info.frostfs.sdk.mappers; +package info.frostfs.sdk.mappers.object; import com.google.protobuf.ByteString; import frostfs.refs.Types; -import info.frostfs.sdk.dto.OwnerId; +import info.frostfs.sdk.dto.object.OwnerId; import static java.util.Objects.isNull; diff --git a/models/src/main/java/info/frostfs/sdk/mappers/response/MetaHeaderMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/response/MetaHeaderMapper.java new file mode 100644 index 0000000..d8e7324 --- /dev/null +++ b/models/src/main/java/info/frostfs/sdk/mappers/response/MetaHeaderMapper.java @@ -0,0 +1,33 @@ +package info.frostfs.sdk.mappers.response; + +import frostfs.session.Types; +import info.frostfs.sdk.dto.response.MetaHeader; +import info.frostfs.sdk.mappers.netmap.VersionMapper; + +import static java.util.Objects.isNull; + +public class MetaHeaderMapper { + private static final String ERROR_META_HEADER_MISSING_ERROR = "MetaHeader is not present"; + + private MetaHeaderMapper() { + } + + public static Types.RequestMetaHeader toGrpcMessage(MetaHeader metaHeader) { + if (isNull(metaHeader)) { + throw new IllegalArgumentException(ERROR_META_HEADER_MISSING_ERROR); + } + + return toGrpcMessageBuilder(metaHeader).build(); + } + + public static Types.RequestMetaHeader.Builder toGrpcMessageBuilder(MetaHeader metaHeader) { + if (isNull(metaHeader)) { + throw new IllegalArgumentException(ERROR_META_HEADER_MISSING_ERROR); + } + + return Types.RequestMetaHeader.newBuilder() + .setVersion(VersionMapper.toGrpcMessage(metaHeader.getVersion())) + .setEpoch(metaHeader.getEpoch()) + .setTtl(metaHeader.getTtl()); + } +} diff --git a/models/src/main/java/info/frostfs/sdk/mappers/StatusMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/response/ResponseStatusMapper.java similarity index 58% rename from models/src/main/java/info/frostfs/sdk/mappers/StatusMapper.java rename to models/src/main/java/info/frostfs/sdk/mappers/response/ResponseStatusMapper.java index 04ddd6e..192ea21 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/StatusMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/response/ResponseStatusMapper.java @@ -1,20 +1,20 @@ -package info.frostfs.sdk.mappers; +package info.frostfs.sdk.mappers.response; import frostfs.status.Types; -import info.frostfs.sdk.dto.Status; +import info.frostfs.sdk.dto.response.ResponseStatus; import info.frostfs.sdk.enums.StatusCode; import static java.util.Objects.isNull; -public class StatusMapper { +public class ResponseStatusMapper { private static final String ERROR_UNKNOWN_VALUE_TEMPLATE = "Unknown StatusCode. Value: %s."; - private StatusMapper() { + private ResponseStatusMapper() { } - public static Status toModel(Types.Status status) { + public static ResponseStatus toModel(Types.Status status) { if (isNull(status)) { - return new Status(StatusCode.SUCCESS); + return new ResponseStatus(StatusCode.SUCCESS); } var statusCode = StatusCode.get(status.getCode()); @@ -24,6 +24,6 @@ public class StatusMapper { ); } - return new Status(statusCode, status.getMessage()); + return new ResponseStatus(statusCode, status.getMessage()); } } diff --git a/models/src/main/java/info/frostfs/sdk/mappers/SignatureMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/response/SignatureMapper.java similarity index 91% rename from models/src/main/java/info/frostfs/sdk/mappers/SignatureMapper.java rename to models/src/main/java/info/frostfs/sdk/mappers/response/SignatureMapper.java index 5e9e103..162d5a5 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/SignatureMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/response/SignatureMapper.java @@ -1,8 +1,8 @@ -package info.frostfs.sdk.mappers; +package info.frostfs.sdk.mappers.response; import com.google.protobuf.ByteString; import frostfs.refs.Types; -import info.frostfs.sdk.dto.Signature; +import info.frostfs.sdk.dto.response.Signature; import static java.util.Objects.isNull; diff --git a/models/src/main/java/info/frostfs/sdk/mappers/SessionMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/session/SessionMapper.java similarity index 96% rename from models/src/main/java/info/frostfs/sdk/mappers/SessionMapper.java rename to models/src/main/java/info/frostfs/sdk/mappers/session/SessionMapper.java index ca14d05..1536f35 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/SessionMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/session/SessionMapper.java @@ -1,4 +1,4 @@ -package info.frostfs.sdk.mappers; +package info.frostfs.sdk.mappers.session; import com.google.protobuf.CodedOutputStream; import com.google.protobuf.InvalidProtocolBufferException; diff --git a/models/src/test/java/info/frostfs/sdk/mappers/MetaHeaderMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/MetaHeaderMapperTest.java deleted file mode 100644 index 7d874cc..0000000 --- a/models/src/test/java/info/frostfs/sdk/mappers/MetaHeaderMapperTest.java +++ /dev/null @@ -1,33 +0,0 @@ -package info.frostfs.sdk.mappers; - -import info.frostfs.sdk.dto.MetaHeader; -import info.frostfs.sdk.dto.Version; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.*; - -public class MetaHeaderMapperTest { - - @Test - void toGrpcMessage_success() { - //Given - var version = new Version(1, 2); - var metaHeader = new MetaHeader(version, 1, 2); - - //When - var result = MetaHeaderMapper.toGrpcMessage(metaHeader); - - //Then - assertNotNull(result); - assertEquals(metaHeader.getEpoch(), result.getEpoch()); - assertEquals(metaHeader.getTtl(), result.getTtl()); - assertEquals(metaHeader.getVersion().getMajor(), result.getVersion().getMajor()); - assertEquals(metaHeader.getVersion().getMinor(), result.getVersion().getMinor()); - } - - @Test - void toGrpcMessage_null() { - //When + Then - assertNull(MetaHeaderMapper.toGrpcMessage(null)); - } -} diff --git a/models/src/test/java/info/frostfs/sdk/mappers/VersionMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/netmap/VersionMapperTest.java similarity index 93% rename from models/src/test/java/info/frostfs/sdk/mappers/VersionMapperTest.java rename to models/src/test/java/info/frostfs/sdk/mappers/netmap/VersionMapperTest.java index 3fea7b7..25aa403 100644 --- a/models/src/test/java/info/frostfs/sdk/mappers/VersionMapperTest.java +++ b/models/src/test/java/info/frostfs/sdk/mappers/netmap/VersionMapperTest.java @@ -1,7 +1,7 @@ -package info.frostfs.sdk.mappers; +package info.frostfs.sdk.mappers.netmap; import frostfs.refs.Types; -import info.frostfs.sdk.dto.Version; +import info.frostfs.sdk.dto.netmap.Version; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; diff --git a/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectFilterMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectFilterMapperTest.java index 3451ccc..7bc8aec 100644 --- a/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectFilterMapperTest.java +++ b/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectFilterMapperTest.java @@ -18,7 +18,7 @@ public class ObjectFilterMapperTest { @EnumSource(value = ObjectMatchType.class) void toGrpcMessage_success(ObjectMatchType type) { //Given - var objectFilter = new ObjectFilter(type, "key", "value"); + var objectFilter = new ObjectFilter.FilterByAttribute(type, "key", "value"); //When var result = ObjectFilterMapper.toGrpcMessage(objectFilter); @@ -40,7 +40,7 @@ public class ObjectFilterMapperTest { @Test void toGrpcMessage_notValidScheme() { //Given - var objectFilter = new ObjectFilter(UNSPECIFIED, "key", "value"); + var objectFilter = new ObjectFilter.FilterByAttribute(UNSPECIFIED, "key", "value"); //When + Then try (MockedStatic mockStatic = mockStatic(Types.MatchType.class)) { diff --git a/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectHeaderMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectHeaderMapperTest.java index dd410b2..a2ca83c 100644 --- a/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectHeaderMapperTest.java +++ b/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectHeaderMapperTest.java @@ -3,16 +3,16 @@ package info.frostfs.sdk.mappers.object; import com.google.protobuf.ByteString; import frostfs.object.Types; import info.frostfs.sdk.dto.container.ContainerId; +import info.frostfs.sdk.dto.netmap.Version; import info.frostfs.sdk.dto.object.ObjectAttribute; import info.frostfs.sdk.dto.object.ObjectHeader; +import info.frostfs.sdk.dto.object.OwnerId; import info.frostfs.sdk.enums.ObjectType; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; import org.mockito.MockedStatic; -import java.util.List; - import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.mockStatic; @@ -27,8 +27,11 @@ public class ObjectHeaderMapperTest { var objectHeader = new ObjectHeader( containerId, type, - List.of(new ObjectAttribute("Filename", "cat.jpg"), new ObjectAttribute("Filename2", "cat2.jpg")) + new ObjectAttribute("Filename", "cat.jpg"), + new ObjectAttribute("Filename2", "cat2.jpg") ); + objectHeader.setOwnerId(new OwnerId("NVxUSpEEJzYXZZtUs18PrJTD9QZkLLNQ8S")); + objectHeader.setVersion(new Version(1, 2)); //When var result = ObjectHeaderMapper.toGrpcMessage(objectHeader); @@ -59,7 +62,7 @@ public class ObjectHeaderMapperTest { void toGrpcMessage_notValidScheme() { //Given var containerId = new ContainerId("EQGx2QeYHJb53uRwYGzcQaW191sZpdNrjutk6veUSV2R"); - var objectHeader = new ObjectHeader(containerId, ObjectType.REGULAR, null); + var objectHeader = new ObjectHeader(containerId, ObjectType.REGULAR); //When + Then try (MockedStatic mockStatic = mockStatic(Types.ObjectType.class)) { diff --git a/models/src/test/java/info/frostfs/sdk/mappers/OwnerIdMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/object/OwnerIdMapperTest.java similarity index 92% rename from models/src/test/java/info/frostfs/sdk/mappers/OwnerIdMapperTest.java rename to models/src/test/java/info/frostfs/sdk/mappers/object/OwnerIdMapperTest.java index bc51895..e9ac8c6 100644 --- a/models/src/test/java/info/frostfs/sdk/mappers/OwnerIdMapperTest.java +++ b/models/src/test/java/info/frostfs/sdk/mappers/object/OwnerIdMapperTest.java @@ -1,6 +1,6 @@ -package info.frostfs.sdk.mappers; +package info.frostfs.sdk.mappers.object; -import info.frostfs.sdk.dto.OwnerId; +import info.frostfs.sdk.dto.object.OwnerId; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; diff --git a/models/src/test/java/info/frostfs/sdk/mappers/response/MetaHeaderMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/response/MetaHeaderMapperTest.java new file mode 100644 index 0000000..3c48b52 --- /dev/null +++ b/models/src/test/java/info/frostfs/sdk/mappers/response/MetaHeaderMapperTest.java @@ -0,0 +1,57 @@ +package info.frostfs.sdk.mappers.response; + +import frostfs.session.Types; +import info.frostfs.sdk.dto.netmap.Version; +import info.frostfs.sdk.dto.response.MetaHeader; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class MetaHeaderMapperTest { + + @Test + void toGrpcMessage_success() { + //Given + var version = new Version(1, 2); + var metaHeader = new MetaHeader(version, 1, 2); + + //When + Types.RequestMetaHeader result = MetaHeaderMapper.toGrpcMessage(metaHeader); + + //Then + assertNotNull(result); + assertEquals(metaHeader.getEpoch(), result.getEpoch()); + assertEquals(metaHeader.getTtl(), result.getTtl()); + assertEquals(metaHeader.getVersion().getMajor(), result.getVersion().getMajor()); + assertEquals(metaHeader.getVersion().getMinor(), result.getVersion().getMinor()); + } + + @Test + void toGrpcMessage_null() { + //When + Then + assertThrows(IllegalArgumentException.class, () -> MetaHeaderMapper.toGrpcMessage(null)); + } + + @Test + void toGrpcMessageBuilder_success() { + //Given + var version = new Version(1, 2); + var metaHeader = new MetaHeader(version, 1, 2); + + //When + Types.RequestMetaHeader.Builder result = MetaHeaderMapper.toGrpcMessageBuilder(metaHeader); + + //Then + assertNotNull(result); + assertEquals(metaHeader.getEpoch(), result.getEpoch()); + assertEquals(metaHeader.getTtl(), result.getTtl()); + assertEquals(metaHeader.getVersion().getMajor(), result.getVersion().getMajor()); + assertEquals(metaHeader.getVersion().getMinor(), result.getVersion().getMinor()); + } + + @Test + void toGrpcMessageBuilder_null() { + //When + Then + assertThrows(IllegalArgumentException.class, () -> MetaHeaderMapper.toGrpcMessageBuilder(null)); + } +} diff --git a/models/src/test/java/info/frostfs/sdk/mappers/StatusMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/response/ResponseStatusMapperTest.java similarity index 80% rename from models/src/test/java/info/frostfs/sdk/mappers/StatusMapperTest.java rename to models/src/test/java/info/frostfs/sdk/mappers/response/ResponseStatusMapperTest.java index d0f5a59..d3073e2 100644 --- a/models/src/test/java/info/frostfs/sdk/mappers/StatusMapperTest.java +++ b/models/src/test/java/info/frostfs/sdk/mappers/response/ResponseStatusMapperTest.java @@ -1,4 +1,4 @@ -package info.frostfs.sdk.mappers; +package info.frostfs.sdk.mappers.response; import frostfs.status.Types; import info.frostfs.sdk.enums.StatusCode; @@ -8,7 +8,7 @@ import org.junit.jupiter.params.provider.EnumSource; import static org.junit.jupiter.api.Assertions.*; -public class StatusMapperTest { +public class ResponseStatusMapperTest { @ParameterizedTest @EnumSource(value = StatusCode.class) @@ -20,7 +20,7 @@ public class StatusMapperTest { .build(); //When - var result = StatusMapper.toModel(status); + var result = ResponseStatusMapper.toModel(status); //Then assertNotNull(result); @@ -31,7 +31,7 @@ public class StatusMapperTest { @Test void toModel_null() { //When - var result = StatusMapper.toModel(null); + var result = ResponseStatusMapper.toModel(null); //Then assertNotNull(result); @@ -48,6 +48,6 @@ public class StatusMapperTest { .build(); //When + Then - assertThrows(IllegalArgumentException.class, () -> StatusMapper.toModel(status)); + assertThrows(IllegalArgumentException.class, () -> ResponseStatusMapper.toModel(status)); } } diff --git a/models/src/test/java/info/frostfs/sdk/mappers/SignatureMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/response/SignatureMapperTest.java similarity index 95% rename from models/src/test/java/info/frostfs/sdk/mappers/SignatureMapperTest.java rename to models/src/test/java/info/frostfs/sdk/mappers/response/SignatureMapperTest.java index 3435ab7..471c0aa 100644 --- a/models/src/test/java/info/frostfs/sdk/mappers/SignatureMapperTest.java +++ b/models/src/test/java/info/frostfs/sdk/mappers/response/SignatureMapperTest.java @@ -1,7 +1,7 @@ -package info.frostfs.sdk.mappers; +package info.frostfs.sdk.mappers.response; import frostfs.refs.Types; -import info.frostfs.sdk.dto.Signature; +import info.frostfs.sdk.dto.response.Signature; import info.frostfs.sdk.enums.SignatureScheme; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; diff --git a/models/src/test/java/info/frostfs/sdk/mappers/SessionMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/session/SessionMapperTest.java similarity index 97% rename from models/src/test/java/info/frostfs/sdk/mappers/SessionMapperTest.java rename to models/src/test/java/info/frostfs/sdk/mappers/session/SessionMapperTest.java index f8c125e..3f7d0ea 100644 --- a/models/src/test/java/info/frostfs/sdk/mappers/SessionMapperTest.java +++ b/models/src/test/java/info/frostfs/sdk/mappers/session/SessionMapperTest.java @@ -1,4 +1,4 @@ -package info.frostfs.sdk.mappers; +package info.frostfs.sdk.mappers.session; import com.google.protobuf.ByteString; import frostfs.session.Types; From 15cf0893c71e5f6356bb827b548fcb50e18ebf63 Mon Sep 17 00:00:00 2001 From: Bruk Ori Date: Wed, 11 Sep 2024 15:33:35 +0300 Subject: [PATCH 16/38] [#12] Remove redundant validation Signed-off-by: Ori Bruk --- .../frostfs/sdk/utils/ValidatorUtils.java | 42 ------------------- .../object/ObjectHeaderMapperTest.java | 3 +- 2 files changed, 2 insertions(+), 43 deletions(-) delete mode 100644 client/src/main/java/info/frostfs/sdk/utils/ValidatorUtils.java diff --git a/client/src/main/java/info/frostfs/sdk/utils/ValidatorUtils.java b/client/src/main/java/info/frostfs/sdk/utils/ValidatorUtils.java deleted file mode 100644 index 20d6b15..0000000 --- a/client/src/main/java/info/frostfs/sdk/utils/ValidatorUtils.java +++ /dev/null @@ -1,42 +0,0 @@ -package info.frostfs.sdk.utils; - -import org.apache.commons.collections4.CollectionUtils; - -import javax.validation.ConstraintViolation; -import javax.validation.Validation; -import javax.validation.ValidationException; -import javax.validation.Validator; -import java.util.Set; - -import static java.util.Objects.isNull; - -public class ValidatorUtils { - private static final String OBJECT_IS_NULL = "object is null"; - private static final String ERROR_PROPERTY_TEMPLATE = "property %s with value %s %s"; - - private static final Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); - - - public static void validate(T object, Class... classes) { - if (isNull(object)) { - throw new ValidationException(OBJECT_IS_NULL); - } - - Set> violations = validator.validate(object, classes); - - if (CollectionUtils.isEmpty(violations)) { - return; - } - - String[] errors = (String[]) violations.stream() - .map(violation -> String.format( - ERROR_PROPERTY_TEMPLATE, - violation.getPropertyPath().toString(), - violation.getInvalidValue(), - violation.getMessage() - )) - .toArray(); - - throw new ValidationException(String.join(",", errors)); - } -} diff --git a/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectHeaderMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectHeaderMapperTest.java index a2ca83c..207854d 100644 --- a/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectHeaderMapperTest.java +++ b/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectHeaderMapperTest.java @@ -8,6 +8,7 @@ import info.frostfs.sdk.dto.object.ObjectAttribute; import info.frostfs.sdk.dto.object.ObjectHeader; import info.frostfs.sdk.dto.object.OwnerId; import info.frostfs.sdk.enums.ObjectType; +import info.frostfs.sdk.mappers.response.MetaHeaderMapper; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; @@ -55,7 +56,7 @@ public class ObjectHeaderMapperTest { @Test void toGrpcMessage_null() { //When + Then - assertNull(ObjectHeaderMapper.toGrpcMessage(null)); + assertThrows(IllegalArgumentException.class, () -> ObjectHeaderMapper.toGrpcMessage(null)); } @Test From 388428af768545f45b3ce326e9b4d4d7bf635be2 Mon Sep 17 00:00:00 2001 From: Bruk Ori Date: Tue, 17 Sep 2024 17:47:03 +0300 Subject: [PATCH 17/38] [#14] add lombok and refactor exceptions. Provide validator. Signed-off-by: Ori Bruk --- README.md | 4 +- client/pom.xml | 6 +- .../java/info/frostfs/sdk/FrostFSClient.java | 23 ++-- .../sdk/annotations/AtLeastOneIsFilled.java | 14 ++ .../frostfs/sdk/annotations/NotBlank.java | 11 ++ .../info/frostfs/sdk/annotations/NotNull.java | 11 ++ .../frostfs/sdk/annotations/Validate.java | 11 ++ .../frostfs/sdk/constants/CryptoConst.java | 2 + .../sdk/exceptions/ResponseException.java | 16 --- .../exceptions/ResponseFrostFSException.java | 19 +++ .../sdk/exceptions/TimeoutException.java | 6 - .../frostfs/sdk/jdo/ClientEnvironment.java | 53 +++----- .../info/frostfs/sdk/jdo/ClientSettings.java | 50 ++----- .../main/java/info/frostfs/sdk/jdo/ECDsa.java | 25 ++-- .../info/frostfs/sdk/jdo/NetworkSettings.java | 125 +----------------- .../frostfs/sdk/jdo/PutObjectParameters.java | 106 ++------------- .../info/frostfs/sdk/jdo/PutObjectResult.java | 18 +-- .../info/frostfs/sdk/jdo/WaitParameters.java | 18 +-- .../frostfs/sdk/services/ContextAccessor.java | 5 +- .../services/impl/ContainerClientImpl.java | 21 ++- .../sdk/services/impl/ObjectClientImpl.java | 35 +++-- .../impl/rwhelper/ObjectReaderImpl.java | 25 +++- .../services/impl/rwhelper/ObjectWriter.java | 14 +- .../info/frostfs/sdk/tools/GrpcClient.java | 25 ++-- .../frostfs/sdk/tools/RequestConstructor.java | 6 +- .../info/frostfs/sdk/tools/RequestSigner.java | 14 +- .../java/info/frostfs/sdk/tools/Verifier.java | 29 ++-- .../info/frostfs/sdk/utils/MessageHelper.java | 8 +- .../info/frostfs/sdk/utils/Validator.java | 120 +++++++++++++++++ cryptography/pom.xml | 11 +- .../java/info/frostfs/sdk/ArrayHelper.java | 7 +- .../main/java/info/frostfs/sdk/Base58.java | 15 ++- .../main/java/info/frostfs/sdk/Helper.java | 11 +- .../java/info/frostfs/sdk/KeyExtension.java | 36 +++-- .../info/frostfs/sdk/ArrayHelperTest.java | 7 +- .../java/info/frostfs/sdk/Base58Test.java | 12 +- .../java/info/frostfs/sdk/HelperTest.java | 11 +- .../info/frostfs/sdk/KeyExtensionTest.java | 37 +++--- exceptions/pom.xml | 20 +++ .../frostfs/sdk/constants/ErrorConst.java | 49 +++++++ .../exceptions/ProcessFrostFSException.java | 11 ++ .../exceptions/TimeoutFrostFSException.java | 6 + .../ValidationFrostFSException.java | 7 + models/pom.xml | 11 +- .../java/info/frostfs/sdk/UuidExtension.java | 15 ++- .../info/frostfs/sdk/constants/AppConst.java | 1 + .../java/info/frostfs/sdk/dto/CheckSum.java | 12 +- .../frostfs/sdk/dto/container/Container.java | 36 +---- .../sdk/dto/container/ContainerId.java | 18 ++- .../sdk/dto/netmap/NetmapSnapshot.java | 19 +-- .../info/frostfs/sdk/dto/netmap/NodeInfo.java | 22 +-- .../sdk/dto/netmap/PlacementPolicy.java | 18 +-- .../info/frostfs/sdk/dto/netmap/Replica.java | 23 ++-- .../info/frostfs/sdk/dto/netmap/Version.java | 18 +-- .../sdk/dto/object/ObjectAttribute.java | 15 +-- .../frostfs/sdk/dto/object/ObjectFilter.java | 37 +----- .../frostfs/sdk/dto/object/ObjectFrostFS.java | 42 ++---- .../frostfs/sdk/dto/object/ObjectHeader.java | 70 +++------- .../info/frostfs/sdk/dto/object/ObjectId.java | 17 ++- .../info/frostfs/sdk/dto/object/OwnerId.java | 18 ++- .../info/frostfs/sdk/dto/object/Split.java | 58 ++------ .../info/frostfs/sdk/dto/object/SplitId.java | 3 + .../frostfs/sdk/dto/response/MetaHeader.java | 40 +++--- .../sdk/dto/response/ResponseStatus.java | 20 +-- .../frostfs/sdk/dto/response/Signature.java | 28 +--- .../frostfs/sdk/dto/session/SessionToken.java | 13 +- .../mappers/container/ContainerMapper.java | 8 +- .../sdk/mappers/netmap/NodeInfoMapper.java | 8 +- .../mappers/netmap/PlacementPolicyMapper.java | 5 +- .../mappers/object/ObjectFilterMapper.java | 12 +- .../mappers/object/ObjectHeaderMapper.java | 23 ++-- .../mappers/response/MetaHeaderMapper.java | 12 +- .../response/ResponseStatusMapper.java | 8 +- .../sdk/mappers/response/SignatureMapper.java | 12 +- .../sdk/mappers/session/SessionMapper.java | 17 ++- .../info/frostfs/sdk/UuidExtensionTest.java | 9 +- .../container/ContainerMapperTest.java | 5 +- .../mappers/netmap/NodeInfoMapperTest.java | 3 +- .../netmap/PlacementPolicyMapperTest.java | 2 +- .../object/ObjectFilterMapperTest.java | 3 +- .../object/ObjectHeaderMapperTest.java | 9 +- .../response/MetaHeaderMapperTest.java | 5 +- .../response/ResponseStatusMapperTest.java | 3 +- .../mappers/response/SignatureMapperTest.java | 3 +- .../mappers/session/SessionMapperTest.java | 7 +- pom.xml | 20 +++ protos/pom.xml | 1 - 87 files changed, 819 insertions(+), 970 deletions(-) create mode 100644 client/src/main/java/info/frostfs/sdk/annotations/AtLeastOneIsFilled.java create mode 100644 client/src/main/java/info/frostfs/sdk/annotations/NotBlank.java create mode 100644 client/src/main/java/info/frostfs/sdk/annotations/NotNull.java create mode 100644 client/src/main/java/info/frostfs/sdk/annotations/Validate.java delete mode 100644 client/src/main/java/info/frostfs/sdk/exceptions/ResponseException.java create mode 100644 client/src/main/java/info/frostfs/sdk/exceptions/ResponseFrostFSException.java delete mode 100644 client/src/main/java/info/frostfs/sdk/exceptions/TimeoutException.java create mode 100644 client/src/main/java/info/frostfs/sdk/utils/Validator.java create mode 100644 exceptions/pom.xml create mode 100644 exceptions/src/main/java/info/frostfs/sdk/constants/ErrorConst.java create mode 100644 exceptions/src/main/java/info/frostfs/sdk/exceptions/ProcessFrostFSException.java create mode 100644 exceptions/src/main/java/info/frostfs/sdk/exceptions/TimeoutFrostFSException.java create mode 100644 exceptions/src/main/java/info/frostfs/sdk/exceptions/ValidationFrostFSException.java diff --git a/README.md b/README.md index fe3c04f..a7cf481 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ public class ContainerExample { FrostFSClient frostFSClient = new FrostFSClient(clientSettings); // Create container - var placementPolicy = new PlacementPolicy(true, new Replica[]{new Replica(1)}); + var placementPolicy = new PlacementPolicy(new Replica[]{new Replica(1)}, true); var containerId = frostFSClient.createContainer(new Container(BasicAcl.PUBLIC_RW, placementPolicy)); // Get container @@ -91,7 +91,7 @@ public class ObjectExample { var objectHeader = frostFSClient.getObjectHead(containerId, objectId); // Search regular objects - var objectIds = frostFSClient.searchObjects(containerId, ObjectFilter.RootFilter()); + var objectIds = frostFSClient.searchObjects(containerId, new ObjectFilter.FilterByRootObject()); } } ``` \ No newline at end of file diff --git a/client/pom.xml b/client/pom.xml index bb808ce..c920473 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -29,9 +29,9 @@ 0.1.0 - org.apache.commons - commons-collections4 - 4.4 + info.frostfs.sdk + exceptions + 0.1.0 commons-codec diff --git a/client/src/main/java/info/frostfs/sdk/FrostFSClient.java b/client/src/main/java/info/frostfs/sdk/FrostFSClient.java index a373559..76d946f 100644 --- a/client/src/main/java/info/frostfs/sdk/FrostFSClient.java +++ b/client/src/main/java/info/frostfs/sdk/FrostFSClient.java @@ -11,24 +11,23 @@ import info.frostfs.sdk.dto.object.ObjectFrostFS; import info.frostfs.sdk.dto.object.ObjectHeader; import info.frostfs.sdk.dto.object.ObjectId; import info.frostfs.sdk.dto.session.SessionToken; +import info.frostfs.sdk.exceptions.ProcessFrostFSException; import info.frostfs.sdk.jdo.ClientEnvironment; import info.frostfs.sdk.jdo.ClientSettings; import info.frostfs.sdk.jdo.NetworkSettings; import info.frostfs.sdk.jdo.PutObjectParameters; import info.frostfs.sdk.services.*; import info.frostfs.sdk.services.impl.*; +import info.frostfs.sdk.utils.Validator; import io.grpc.Channel; import java.util.List; +import static info.frostfs.sdk.constants.ErrorConst.VERSION_UNSUPPORTED_TEMPLATE; import static info.frostfs.sdk.tools.GrpcClient.initGrpcChannel; -import static java.util.Objects.isNull; import static java.util.Objects.nonNull; public class FrostFSClient implements ContainerClient, ObjectClient, NetmapClient, SessionClient, ToolsClient { - private static final String ERROR_CLIENT_OPTIONS_INIT = "Options must be initialized."; - private static final String ERROR_VERSION_SUPPORT_TEMPLATE = "FrostFS %s is not supported."; - private final ContainerClientImpl containerClientImpl; private final NetmapClientImpl netmapClientImpl; private final ObjectClientImpl objectClientImpl; @@ -36,16 +35,16 @@ public class FrostFSClient implements ContainerClient, ObjectClient, NetmapClien private final ObjectToolsImpl objectToolsImpl; public FrostFSClient(ClientSettings clientSettings) { - if (isNull(clientSettings)) { - throw new IllegalArgumentException(ERROR_CLIENT_OPTIONS_INIT); - } - - Channel channel = nonNull(clientSettings.getChannel()) ? clientSettings.getChannel() - : initGrpcChannel(clientSettings.getHost(), clientSettings.getCredentials()); + Validator.validate(clientSettings); + Channel channel = nonNull(clientSettings.getChannel()) + ? clientSettings.getChannel() + : initGrpcChannel(clientSettings); ClientEnvironment clientEnvironment = new ClientEnvironment(clientSettings.getKey(), channel, new Version(), this); + Validator.validate(clientEnvironment); + this.containerClientImpl = new ContainerClientImpl(clientEnvironment); this.netmapClientImpl = new NetmapClientImpl(clientEnvironment); this.sessionClientImpl = new SessionClientImpl(clientEnvironment); @@ -57,8 +56,8 @@ public class FrostFSClient implements ContainerClient, ObjectClient, NetmapClien private void checkFrostFsVersionSupport(Version version) { var localNodeInfo = netmapClientImpl.getLocalNodeInfo(); if (!localNodeInfo.getVersion().isSupported(version)) { - throw new IllegalArgumentException( - String.format(ERROR_VERSION_SUPPORT_TEMPLATE, localNodeInfo.getVersion()) + throw new ProcessFrostFSException( + String.format(VERSION_UNSUPPORTED_TEMPLATE, localNodeInfo.getVersion()) ); } } diff --git a/client/src/main/java/info/frostfs/sdk/annotations/AtLeastOneIsFilled.java b/client/src/main/java/info/frostfs/sdk/annotations/AtLeastOneIsFilled.java new file mode 100644 index 0000000..e10c4e6 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/annotations/AtLeastOneIsFilled.java @@ -0,0 +1,14 @@ +package info.frostfs.sdk.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE}) +public @interface AtLeastOneIsFilled { + String message() default "At least one of the fields (%s) must be filled in"; + + String[] fields(); +} diff --git a/client/src/main/java/info/frostfs/sdk/annotations/NotBlank.java b/client/src/main/java/info/frostfs/sdk/annotations/NotBlank.java new file mode 100644 index 0000000..8f8c234 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/annotations/NotBlank.java @@ -0,0 +1,11 @@ +package info.frostfs.sdk.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface NotBlank { +} diff --git a/client/src/main/java/info/frostfs/sdk/annotations/NotNull.java b/client/src/main/java/info/frostfs/sdk/annotations/NotNull.java new file mode 100644 index 0000000..2780c70 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/annotations/NotNull.java @@ -0,0 +1,11 @@ +package info.frostfs.sdk.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface NotNull { +} \ No newline at end of file diff --git a/client/src/main/java/info/frostfs/sdk/annotations/Validate.java b/client/src/main/java/info/frostfs/sdk/annotations/Validate.java new file mode 100644 index 0000000..ee412b6 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/annotations/Validate.java @@ -0,0 +1,11 @@ +package info.frostfs.sdk.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface Validate { +} diff --git a/client/src/main/java/info/frostfs/sdk/constants/CryptoConst.java b/client/src/main/java/info/frostfs/sdk/constants/CryptoConst.java index 4e28686..8865173 100644 --- a/client/src/main/java/info/frostfs/sdk/constants/CryptoConst.java +++ b/client/src/main/java/info/frostfs/sdk/constants/CryptoConst.java @@ -2,6 +2,8 @@ package info.frostfs.sdk.constants; public class CryptoConst { public static final String SIGNATURE_ALGORITHM = "NONEwithECDSAinP1363Format"; + public static final int RFC6979_SIGNATURE_SIZE = 64; + public static final int HASH_SIGNATURE_SIZE = 65; private CryptoConst() { } diff --git a/client/src/main/java/info/frostfs/sdk/exceptions/ResponseException.java b/client/src/main/java/info/frostfs/sdk/exceptions/ResponseException.java deleted file mode 100644 index 1367bd1..0000000 --- a/client/src/main/java/info/frostfs/sdk/exceptions/ResponseException.java +++ /dev/null @@ -1,16 +0,0 @@ -package info.frostfs.sdk.exceptions; - -import info.frostfs.sdk.dto.response.ResponseStatus; - -public class ResponseException extends RuntimeException { - private final ResponseStatus status; - - public ResponseException(ResponseStatus status) { - super(status.toString()); - this.status = status; - } - - public ResponseStatus getStatus() { - return status; - } -} diff --git a/client/src/main/java/info/frostfs/sdk/exceptions/ResponseFrostFSException.java b/client/src/main/java/info/frostfs/sdk/exceptions/ResponseFrostFSException.java new file mode 100644 index 0000000..6b216a6 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/exceptions/ResponseFrostFSException.java @@ -0,0 +1,19 @@ +package info.frostfs.sdk.exceptions; + +import info.frostfs.sdk.dto.response.ResponseStatus; +import lombok.Getter; + +@Getter +public class ResponseFrostFSException extends RuntimeException { + private final ResponseStatus status; + + public ResponseFrostFSException(ResponseStatus status) { + super(status.toString()); + this.status = status; + } + + public ResponseFrostFSException(String message) { + super(message); + this.status = null; + } +} diff --git a/client/src/main/java/info/frostfs/sdk/exceptions/TimeoutException.java b/client/src/main/java/info/frostfs/sdk/exceptions/TimeoutException.java deleted file mode 100644 index b619ddd..0000000 --- a/client/src/main/java/info/frostfs/sdk/exceptions/TimeoutException.java +++ /dev/null @@ -1,6 +0,0 @@ -package info.frostfs.sdk.exceptions; - -public class TimeoutException extends RuntimeException { - public TimeoutException() { - } -} diff --git a/client/src/main/java/info/frostfs/sdk/jdo/ClientEnvironment.java b/client/src/main/java/info/frostfs/sdk/jdo/ClientEnvironment.java index 22bc8cd..34eca67 100644 --- a/client/src/main/java/info/frostfs/sdk/jdo/ClientEnvironment.java +++ b/client/src/main/java/info/frostfs/sdk/jdo/ClientEnvironment.java @@ -1,60 +1,41 @@ package info.frostfs.sdk.jdo; import info.frostfs.sdk.FrostFSClient; +import info.frostfs.sdk.annotations.NotNull; +import info.frostfs.sdk.annotations.Validate; import info.frostfs.sdk.dto.netmap.Version; import info.frostfs.sdk.dto.object.OwnerId; import io.grpc.Channel; -import org.apache.commons.lang3.StringUtils; - -import static java.util.Objects.isNull; +import lombok.Getter; +import lombok.Setter; +@Getter +@Setter public class ClientEnvironment { - private static final String ERROR_MESSAGE = "One of the input attributes is missing"; + @NotNull private final OwnerId ownerId; + + @NotNull private final Version version; + + @NotNull + @Validate private final ECDsa key; + + @NotNull private final Channel channel; + + @NotNull private final FrostFSClient frostFSClient; + private NetworkSettings networkSettings; public ClientEnvironment(String wif, Channel channel, Version version, FrostFSClient frostFSClient) { - if (StringUtils.isEmpty(wif) || isNull(channel) || isNull(version) || isNull(frostFSClient)) { - throw new IllegalArgumentException(ERROR_MESSAGE); - } - this.key = new ECDsa(wif); this.ownerId = new OwnerId(key.getPublicKeyByte()); this.version = version; this.channel = channel; this.frostFSClient = frostFSClient; } - - public Channel getChannel() { - return channel; - } - - public NetworkSettings getNetworkSettings() { - return networkSettings; - } - - public void setNetworkSettings(NetworkSettings networkSettings) { - this.networkSettings = networkSettings; - } - - public FrostFSClient getFrostFSClient() { - return frostFSClient; - } - - public OwnerId getOwnerId() { - return ownerId; - } - - public Version getVersion() { - return version; - } - - public ECDsa getKey() { - return key; - } } diff --git a/client/src/main/java/info/frostfs/sdk/jdo/ClientSettings.java b/client/src/main/java/info/frostfs/sdk/jdo/ClientSettings.java index a4962a0..bca70ce 100644 --- a/client/src/main/java/info/frostfs/sdk/jdo/ClientSettings.java +++ b/client/src/main/java/info/frostfs/sdk/jdo/ClientSettings.java @@ -1,17 +1,20 @@ package info.frostfs.sdk.jdo; +import info.frostfs.sdk.annotations.AtLeastOneIsFilled; +import info.frostfs.sdk.annotations.NotNull; import io.grpc.Channel; import io.grpc.ChannelCredentials; -import org.apache.commons.lang3.StringUtils; - -import static java.util.Objects.isNull; +import lombok.Getter; +import lombok.experimental.FieldNameConstants; +@Getter +@FieldNameConstants +@AtLeastOneIsFilled(fields = {ClientSettings.Fields.host, ClientSettings.Fields.channel}) public class ClientSettings { - private static final String ERROR_TEMPLATE = "%s required parameter."; - private static final String KEY_NAME = "Key"; - private static final String HOST_AND_CHANNEL_NAME = "Host or Channel"; + @NotNull private final String key; + private String host; private ChannelCredentials credentials; private Channel channel; @@ -19,51 +22,16 @@ public class ClientSettings { public ClientSettings(String key, String host) { this.key = key; this.host = host; - validate(); } public ClientSettings(String key, String host, ChannelCredentials credentials) { this.key = key; this.host = host; this.credentials = credentials; - validate(); } public ClientSettings(String key, Channel channel) { this.key = key; this.channel = channel; - validate(); - } - - public Channel getChannel() { - return channel; - } - - public ChannelCredentials getCredentials() { - return credentials; - } - - public String getHost() { - return host; - } - - public String getKey() { - return key; - } - - public void validate() { - StringBuilder errorMessage = new StringBuilder(); - - if (StringUtils.isEmpty(key)) { - errorMessage.append(String.format(ERROR_TEMPLATE, KEY_NAME)).append(System.lineSeparator()); - } - - if (StringUtils.isEmpty(host) && isNull(channel)) { - errorMessage.append(String.format(ERROR_TEMPLATE, HOST_AND_CHANNEL_NAME)).append(System.lineSeparator()); - } - - if (errorMessage.length() != 0) { - throw new IllegalArgumentException(errorMessage.toString()); - } } } diff --git a/client/src/main/java/info/frostfs/sdk/jdo/ECDsa.java b/client/src/main/java/info/frostfs/sdk/jdo/ECDsa.java index 8e86d7c..b7e4686 100644 --- a/client/src/main/java/info/frostfs/sdk/jdo/ECDsa.java +++ b/client/src/main/java/info/frostfs/sdk/jdo/ECDsa.java @@ -1,37 +1,34 @@ package info.frostfs.sdk.jdo; +import info.frostfs.sdk.annotations.NotNull; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; +import lombok.Getter; import org.apache.commons.lang3.StringUtils; import java.security.PrivateKey; import static info.frostfs.sdk.KeyExtension.*; +import static info.frostfs.sdk.constants.ErrorConst.WIF_IS_INVALID; +@Getter public class ECDsa { - private static final String ERROR_MESSAGE = "WIF is invalid"; + @NotNull private final byte[] publicKeyByte; + + @NotNull private final byte[] privateKeyByte; + + @NotNull private final PrivateKey privateKey; public ECDsa(String wif) { if (StringUtils.isEmpty(wif)) { - throw new IllegalArgumentException(ERROR_MESSAGE); + throw new ValidationFrostFSException(WIF_IS_INVALID); } this.privateKeyByte = getPrivateKeyFromWIF(wif); this.publicKeyByte = loadPublicKey(privateKeyByte); this.privateKey = loadPrivateKey(privateKeyByte); } - - public byte[] getPublicKeyByte() { - return publicKeyByte; - } - - public byte[] getPrivateKeyByte() { - return privateKeyByte; - } - - public PrivateKey getPrivateKey() { - return privateKey; - } } diff --git a/client/src/main/java/info/frostfs/sdk/jdo/NetworkSettings.java b/client/src/main/java/info/frostfs/sdk/jdo/NetworkSettings.java index daf9981..103cb64 100644 --- a/client/src/main/java/info/frostfs/sdk/jdo/NetworkSettings.java +++ b/client/src/main/java/info/frostfs/sdk/jdo/NetworkSettings.java @@ -1,8 +1,13 @@ package info.frostfs.sdk.jdo; +import lombok.Getter; +import lombok.Setter; + import java.util.HashMap; import java.util.Map; +@Getter +@Setter public class NetworkSettings { private Long auditFee; @@ -20,124 +25,4 @@ public class NetworkSettings { private Boolean homomorphicHashingDisabled; private Boolean maintenanceModeAllowed; private Map unnamedSettings = new HashMap<>(); - - public Long getAuditFee() { - return auditFee; - } - - public void setAuditFee(Long auditFee) { - this.auditFee = auditFee; - } - - public Long getBasicIncomeRate() { - return basicIncomeRate; - } - - public void setBasicIncomeRate(Long basicIncomeRate) { - this.basicIncomeRate = basicIncomeRate; - } - - public long getContainerFee() { - return containerFee; - } - - public void setContainerFee(long containerFee) { - this.containerFee = containerFee; - } - - public long getContainerAliasFee() { - return containerAliasFee; - } - - public void setContainerAliasFee(long containerAliasFee) { - this.containerAliasFee = containerAliasFee; - } - - public long getInnerRingCandidateFee() { - return innerRingCandidateFee; - } - - public void setInnerRingCandidateFee(long innerRingCandidateFee) { - this.innerRingCandidateFee = innerRingCandidateFee; - } - - public long getWithdrawFee() { - return withdrawFee; - } - - public void setWithdrawFee(long withdrawFee) { - this.withdrawFee = withdrawFee; - } - - public long getEpochDuration() { - return epochDuration; - } - - public void setEpochDuration(long epochDuration) { - this.epochDuration = epochDuration; - } - - public long getIRCandidateFee() { - return iRCandidateFee; - } - - public void setIRCandidateFee(long iRCandidateFee) { - this.iRCandidateFee = iRCandidateFee; - } - - public long getMaxObjectSize() { - return maxObjectSize; - } - - public void setMaxObjectSize(long maxObjectSize) { - this.maxObjectSize = maxObjectSize; - } - - public long getMaxECDataCount() { - return maxECDataCount; - } - - public void setMaxECDataCount(long maxECDataCount) { - this.maxECDataCount = maxECDataCount; - } - - public long getMaxECParityCount() { - return maxECParityCount; - } - - public void setMaxECParityCount(long maxECParityCount) { - this.maxECParityCount = maxECParityCount; - } - - public long getWithdrawalFee() { - return withdrawalFee; - } - - public void setWithdrawalFee(long withdrawalFee) { - this.withdrawalFee = withdrawalFee; - } - - public boolean isHomomorphicHashingDisabled() { - return homomorphicHashingDisabled; - } - - public void setHomomorphicHashingDisabled(boolean homomorphicHashingDisabled) { - this.homomorphicHashingDisabled = homomorphicHashingDisabled; - } - - public boolean isMaintenanceModeAllowed() { - return maintenanceModeAllowed; - } - - public void setMaintenanceModeAllowed(boolean maintenanceModeAllowed) { - this.maintenanceModeAllowed = maintenanceModeAllowed; - } - - public Map getUnnamedSettings() { - return unnamedSettings; - } - - public void setUnnamedSettings(Map unnamedSettings) { - this.unnamedSettings = unnamedSettings; - } } diff --git a/client/src/main/java/info/frostfs/sdk/jdo/PutObjectParameters.java b/client/src/main/java/info/frostfs/sdk/jdo/PutObjectParameters.java index b091cb9..d834cd9 100644 --- a/client/src/main/java/info/frostfs/sdk/jdo/PutObjectParameters.java +++ b/client/src/main/java/info/frostfs/sdk/jdo/PutObjectParameters.java @@ -1,19 +1,23 @@ package info.frostfs.sdk.jdo; +import info.frostfs.sdk.annotations.NotNull; import info.frostfs.sdk.dto.object.ObjectHeader; import info.frostfs.sdk.dto.session.SessionToken; +import lombok.Getter; +import lombok.Setter; import java.io.FileInputStream; -import static java.util.Objects.isNull; - +@Getter +@Setter public class PutObjectParameters { - private static final String ERROR_TEMPLATE = "%s value cannot be null."; - private static final String HEADER_NAME = "Header"; - private static final String PAYLOAD_NAME = "Payload"; + @NotNull private ObjectHeader header; + + @NotNull private FileInputStream payload; + private boolean clientCut; private int bufferMaxSize; private byte[] customerBuffer; @@ -27,102 +31,10 @@ public class PutObjectParameters { this.payload = payload; this.clientCut = clientCut; this.bufferMaxSize = bufferMaxSize; - - validate(); } public PutObjectParameters(ObjectHeader header, FileInputStream payload) { this.header = header; this.payload = payload; - - validate(); - } - - public byte[] getCustomerBuffer() { - return customerBuffer; - } - - public void setCustomerBuffer(byte[] customerBuffer) { - this.customerBuffer = customerBuffer; - } - - public SessionToken getSessionToken() { - return sessionToken; - } - - public void setSessionToken(SessionToken sessionToken) { - this.sessionToken = sessionToken; - } - - public int getMaxObjectSizeCache() { - return maxObjectSizeCache; - } - - public void setMaxObjectSizeCache(int maxObjectSizeCache) { - this.maxObjectSizeCache = maxObjectSizeCache; - } - - public long getCurrentStreamPosition() { - return currentStreamPosition; - } - - public void setCurrentStreamPosition(long currentStreamPosition) { - this.currentStreamPosition = currentStreamPosition; - } - - public long getFullLength() { - return fullLength; - } - - public void setFullLength(long fullLength) { - this.fullLength = fullLength; - } - - public ObjectHeader getHeader() { - return header; - } - - public void setHeader(ObjectHeader header) { - this.header = header; - } - - public FileInputStream getPayload() { - return payload; - } - - public void setPayload(FileInputStream payload) { - this.payload = payload; - } - - public boolean isClientCut() { - return clientCut; - } - - public void setClientCut(boolean clientCut) { - this.clientCut = clientCut; - } - - public int getBufferMaxSize() { - return bufferMaxSize; - } - - public void setBufferMaxSize(int bufferMaxSize) { - this.bufferMaxSize = bufferMaxSize; - } - - public void validate() { - StringBuilder errorMessage = new StringBuilder(); - - if (isNull(header)) { - errorMessage.append(String.format(ERROR_TEMPLATE, HEADER_NAME)).append(System.lineSeparator()); - } - - if (isNull(payload)) { - errorMessage.append(String.format(ERROR_TEMPLATE, PAYLOAD_NAME)).append(System.lineSeparator()); - } - - if (errorMessage.length() != 0) { - throw new IllegalArgumentException(errorMessage.toString()); - } } } diff --git a/client/src/main/java/info/frostfs/sdk/jdo/PutObjectResult.java b/client/src/main/java/info/frostfs/sdk/jdo/PutObjectResult.java index 24ee14b..4fb933b 100644 --- a/client/src/main/java/info/frostfs/sdk/jdo/PutObjectResult.java +++ b/client/src/main/java/info/frostfs/sdk/jdo/PutObjectResult.java @@ -1,21 +1,13 @@ package info.frostfs.sdk.jdo; import info.frostfs.sdk.dto.object.ObjectId; +import lombok.AllArgsConstructor; +import lombok.Getter; +@Getter +@AllArgsConstructor public class PutObjectResult { + private final ObjectId objectId; private final int objectSize; - - public PutObjectResult(ObjectId objectId, int objectSize) { - this.objectId = objectId; - this.objectSize = objectSize; - } - - public ObjectId getObjectId() { - return objectId; - } - - public int getObjectSize() { - return objectSize; - } } diff --git a/client/src/main/java/info/frostfs/sdk/jdo/WaitParameters.java b/client/src/main/java/info/frostfs/sdk/jdo/WaitParameters.java index 0aa8c47..7fdd965 100644 --- a/client/src/main/java/info/frostfs/sdk/jdo/WaitParameters.java +++ b/client/src/main/java/info/frostfs/sdk/jdo/WaitParameters.java @@ -1,8 +1,13 @@ package info.frostfs.sdk.jdo; +import lombok.AllArgsConstructor; +import lombok.Getter; + import java.time.Duration; import java.time.LocalDateTime; +@Getter +@AllArgsConstructor public class WaitParameters { private static final Duration DEFAULT_TIMEOUT = Duration.ofSeconds(120); private static final Duration DEFAULT_POLL_INTERVAL = Duration.ofSeconds(5); @@ -15,19 +20,6 @@ public class WaitParameters { this.pollInterval = DEFAULT_POLL_INTERVAL; } - public WaitParameters(Duration timeout, Duration pollInterval) { - this.timeout = timeout; - this.pollInterval = pollInterval; - } - - public Duration getTimeout() { - return timeout; - } - - public Duration getPollInterval() { - return pollInterval; - } - public LocalDateTime getDeadline() { return LocalDateTime.now().plus(timeout); } diff --git a/client/src/main/java/info/frostfs/sdk/services/ContextAccessor.java b/client/src/main/java/info/frostfs/sdk/services/ContextAccessor.java index 72c47dd..3658609 100644 --- a/client/src/main/java/info/frostfs/sdk/services/ContextAccessor.java +++ b/client/src/main/java/info/frostfs/sdk/services/ContextAccessor.java @@ -1,7 +1,9 @@ package info.frostfs.sdk.services; import info.frostfs.sdk.jdo.ClientEnvironment; +import lombok.Getter; +@Getter public class ContextAccessor { private final ClientEnvironment context; @@ -9,7 +11,4 @@ public class ContextAccessor { this.context = context; } - public ClientEnvironment getContext() { - return context; - } } diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/ContainerClientImpl.java b/client/src/main/java/info/frostfs/sdk/services/impl/ContainerClientImpl.java index db7c0bf..77468af 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/ContainerClientImpl.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/ContainerClientImpl.java @@ -8,8 +8,9 @@ import info.frostfs.sdk.dto.container.ContainerId; import info.frostfs.sdk.dto.session.SessionToken; import info.frostfs.sdk.enums.StatusCode; import info.frostfs.sdk.enums.WaitExpects; -import info.frostfs.sdk.exceptions.ResponseException; -import info.frostfs.sdk.exceptions.TimeoutException; +import info.frostfs.sdk.exceptions.ResponseFrostFSException; +import info.frostfs.sdk.exceptions.TimeoutFrostFSException; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; import info.frostfs.sdk.jdo.ClientEnvironment; import info.frostfs.sdk.jdo.WaitParameters; import info.frostfs.sdk.mappers.container.ContainerIdMapper; @@ -28,12 +29,10 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; +import static info.frostfs.sdk.constants.ErrorConst.PARAM_IS_MISSING_TEMPLATE; import static java.util.Objects.isNull; public class ContainerClientImpl extends ContextAccessor implements ContainerClient { - private static final String ERROR_CONTAINER_ID_MISSING = "ContainerId is not present"; - private static final String ERROR_CONTAINER_MISSING = "Container is not present"; - private final ContainerServiceGrpc.ContainerServiceBlockingStub serviceBlockingStub; private final SessionToolsImpl sessionTools; @@ -50,7 +49,7 @@ public class ContainerClientImpl extends ContextAccessor implements ContainerCli @Override public Container getContainer(ContainerId cid) { if (isNull(cid)) { - throw new IllegalArgumentException(ERROR_CONTAINER_ID_MISSING); + throw new ValidationFrostFSException(String.format(PARAM_IS_MISSING_TEMPLATE, ContainerId.class.getName())); } var request = createGetRequest(ContainerIdMapper.toGrpcMessage(cid), null); @@ -77,7 +76,7 @@ public class ContainerClientImpl extends ContextAccessor implements ContainerCli @Override public ContainerId createContainer(Container container) { if (isNull(container)) { - throw new IllegalArgumentException(ERROR_CONTAINER_MISSING); + throw new ValidationFrostFSException(String.format(PARAM_IS_MISSING_TEMPLATE, Container.class.getName())); } var grpcContainer = ContainerMapper.toGrpcMessage(container); @@ -95,7 +94,7 @@ public class ContainerClientImpl extends ContextAccessor implements ContainerCli @Override public void deleteContainer(ContainerId cid) { if (isNull(cid)) { - throw new IllegalArgumentException(ERROR_CONTAINER_ID_MISSING); + throw new ValidationFrostFSException(String.format(PARAM_IS_MISSING_TEMPLATE, ContainerId.class.getName())); } var grpcContainerId = ContainerIdMapper.toGrpcMessage(cid); @@ -124,13 +123,13 @@ public class ContainerClientImpl extends ContextAccessor implements ContainerCli } if (LocalDateTime.now().isAfter(deadLine)) { - throw new TimeoutException(); + throw new TimeoutFrostFSException(); } WaitUtil.sleep(waitParams.getPollInterval().toMillis()); - } catch (ResponseException exp) { + } catch (ResponseFrostFSException exp) { if (LocalDateTime.now().isAfter(deadLine)) { - throw new TimeoutException(); + throw new TimeoutFrostFSException(); } if (exp.getStatus().getCode() != StatusCode.CONTAINER_NOT_FOUND) { diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/ObjectClientImpl.java b/client/src/main/java/info/frostfs/sdk/services/impl/ObjectClientImpl.java index 736189e..3979020 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/ObjectClientImpl.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/ObjectClientImpl.java @@ -10,6 +10,8 @@ import info.frostfs.sdk.dto.container.ContainerId; import info.frostfs.sdk.dto.object.*; import info.frostfs.sdk.dto.session.SessionToken; import info.frostfs.sdk.enums.ObjectType; +import info.frostfs.sdk.exceptions.ProcessFrostFSException; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; import info.frostfs.sdk.jdo.ClientEnvironment; import info.frostfs.sdk.jdo.PutObjectParameters; import info.frostfs.sdk.jdo.PutObjectResult; @@ -26,6 +28,7 @@ import info.frostfs.sdk.services.impl.rwhelper.ObjectWriter; import info.frostfs.sdk.services.impl.rwhelper.SearchReader; import info.frostfs.sdk.tools.RequestConstructor; import info.frostfs.sdk.tools.Verifier; +import info.frostfs.sdk.utils.Validator; import org.apache.commons.collections4.CollectionUtils; import java.io.FileInputStream; @@ -34,12 +37,11 @@ import java.util.ArrayList; import java.util.List; import static info.frostfs.sdk.Helper.getSha256; +import static info.frostfs.sdk.constants.ErrorConst.*; import static info.frostfs.sdk.tools.RequestSigner.sign; import static java.util.Objects.isNull; public class ObjectClientImpl extends ContextAccessor implements ObjectClient { - private static final String ERROR_PARAMS_MISSING = "Params is not present"; - private final ObjectServiceGrpc.ObjectServiceBlockingStub objectServiceBlockingClient; private final ObjectServiceGrpc.ObjectServiceStub objectServiceClient; private final ObjectToolsImpl objectToolsImpl; @@ -60,7 +62,12 @@ public class ObjectClientImpl extends ContextAccessor implements ObjectClient { @Override public ObjectHeader getObjectHead(ContainerId cid, ObjectId oid) { if (isNull(cid) || isNull(oid)) { - throw new IllegalArgumentException(ERROR_PARAMS_MISSING); + throw new ValidationFrostFSException( + String.format( + PARAMS_ARE_MISSING_TEMPLATE, + String.join(FIELDS_DELIMITER_COMMA, ContainerId.class.getName(), ObjectId.class.getName()) + ) + ); } var request = createHeadRequest(ContainerIdMapper.toGrpcMessage(cid), ObjectIdMapper.toGrpcMessage(oid)); @@ -89,7 +96,7 @@ public class ObjectClientImpl extends ContextAccessor implements ObjectClient { } @Override - public Iterable searchObjects(ContainerId cid, ObjectFilter... filters) { + public Iterable searchObjects(ContainerId cid, ObjectFilter... filters) { var request = createSearchRequest(ContainerIdMapper.toGrpcMessage(cid), filters); var objectsIds = searchObjects(request); @@ -99,7 +106,7 @@ public class ObjectClientImpl extends ContextAccessor implements ObjectClient { @Override public ObjectId putObject(PutObjectParameters parameters) { - parameters.validate(); + Validator.validate(parameters); if (parameters.isClientCut()) { return putClientCutObject(parameters); @@ -140,7 +147,9 @@ public class ObjectClientImpl extends ContextAccessor implements ObjectClient { private ObjectReaderImpl getObjectInit(Service.GetRequest initRequest) { if (initRequest.getSerializedSize() == 0) { - throw new IllegalArgumentException(initRequest.getClass().getName()); + throw new ProcessFrostFSException( + String.format(PROTO_MESSAGE_IS_EMPTY_TEMPLATE, initRequest.getClass().getName()) + ); } return new ObjectReaderImpl(objectServiceBlockingClient.get(initRequest)); @@ -209,7 +218,7 @@ public class ObjectClientImpl extends ContextAccessor implements ObjectClient { if (parameters.getMaxObjectSizeCache() == 0) { var networkSettings = getContext().getFrostFSClient().getNetworkSettings(); - parameters.setMaxObjectSizeCache((int) networkSettings.getMaxObjectSize()); + parameters.setMaxObjectSizeCache(networkSettings.getMaxObjectSize().intValue()); } var restBytes = fullLength - parameters.getCurrentStreamPosition(); @@ -287,7 +296,9 @@ public class ObjectClientImpl extends ContextAccessor implements ObjectClient { private ObjectWriter putObjectInit(Service.PutRequest initRequest) { if (initRequest.getSerializedSize() == 0) { - throw new IllegalArgumentException(initRequest.getClass().getName()); + throw new ProcessFrostFSException( + String.format(PROTO_MESSAGE_IS_EMPTY_TEMPLATE, initRequest.getClass().getName()) + ); } ObjectWriter writer = new ObjectWriter(objectServiceClient); @@ -310,7 +321,9 @@ public class ObjectClientImpl extends ContextAccessor implements ObjectClient { private SearchReader getSearchReader(Service.SearchRequest initRequest) { if (initRequest.getSerializedSize() == 0) { - throw new IllegalArgumentException(initRequest.getClass().getName()); + throw new ProcessFrostFSException( + String.format(PROTO_MESSAGE_IS_EMPTY_TEMPLATE, initRequest.getClass().getName()) + ); } return new SearchReader(objectServiceBlockingClient.search(initRequest)); @@ -320,7 +333,7 @@ public class ObjectClientImpl extends ContextAccessor implements ObjectClient { try { return fileInputStream.readNBytes(buffer, 0, size); } catch (IOException exp) { - throw new IllegalArgumentException(exp.getMessage()); + throw new ProcessFrostFSException(exp.getMessage()); } } @@ -328,7 +341,7 @@ public class ObjectClientImpl extends ContextAccessor implements ObjectClient { try { return fileInputStream.getChannel().size(); } catch (IOException exp) { - throw new IllegalArgumentException(exp.getMessage()); + throw new ProcessFrostFSException(exp.getMessage()); } } diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectReaderImpl.java b/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectReaderImpl.java index b5f50d4..5b53c7c 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectReaderImpl.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectReaderImpl.java @@ -3,14 +3,15 @@ package info.frostfs.sdk.services.impl.rwhelper; import frostfs.object.Service; import frostfs.object.Types; import info.frostfs.sdk.dto.object.ObjectReader; +import info.frostfs.sdk.exceptions.ProcessFrostFSException; import info.frostfs.sdk.tools.Verifier; import java.util.Iterator; -public class ObjectReaderImpl implements ObjectReader { - public static final String ERROR_UNEXPECTED_STREAM = "unexpected end of stream"; - public static final String ERROR_UNEXPECTED_MESSAGE_TYPE = "unexpected message type"; +import static info.frostfs.sdk.constants.ErrorConst.UNEXPECTED_MESSAGE_TYPE_TEMPLATE; +import static info.frostfs.sdk.constants.ErrorConst.UNEXPECTED_STREAM; +public class ObjectReaderImpl implements ObjectReader { public Iterator call; public ObjectReaderImpl(Iterator call) { @@ -19,14 +20,20 @@ public class ObjectReaderImpl implements ObjectReader { public Types.Object readHeader() { if (!call.hasNext()) { - throw new IllegalArgumentException(ERROR_UNEXPECTED_STREAM); + throw new ProcessFrostFSException(UNEXPECTED_STREAM); } var response = call.next(); Verifier.checkResponse(response); if (response.getBody().getObjectPartCase().getNumber() != Service.GetResponse.Body.INIT_FIELD_NUMBER) { - throw new IllegalArgumentException(ERROR_UNEXPECTED_MESSAGE_TYPE); + throw new ProcessFrostFSException( + String.format( + UNEXPECTED_MESSAGE_TYPE_TEMPLATE, + Service.GetResponse.Body.INIT_FIELD_NUMBER, + response.getBody().getObjectPartCase().getNumber() + ) + ); } return Types.Object.newBuilder() @@ -44,7 +51,13 @@ public class ObjectReaderImpl implements ObjectReader { Verifier.checkResponse(response); if (response.getBody().getObjectPartCase().getNumber() != Service.GetResponse.Body.CHUNK_FIELD_NUMBER) { - throw new IllegalArgumentException(ERROR_UNEXPECTED_MESSAGE_TYPE); + throw new ProcessFrostFSException( + String.format( + UNEXPECTED_MESSAGE_TYPE_TEMPLATE, + Service.GetResponse.Body.CHUNK_FIELD_NUMBER, + response.getBody().getObjectPartCase().getNumber() + ) + ); } return response.getBody().getChunk().toByteArray(); diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectWriter.java b/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectWriter.java index 110df20..f082507 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectWriter.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectWriter.java @@ -2,9 +2,12 @@ package info.frostfs.sdk.services.impl.rwhelper; import frostfs.object.ObjectServiceGrpc; import frostfs.object.Service; +import info.frostfs.sdk.exceptions.ProcessFrostFSException; import info.frostfs.sdk.utils.WaitUtil; import io.grpc.stub.StreamObserver; +import lombok.Getter; +import static info.frostfs.sdk.constants.ErrorConst.PROTO_MESSAGE_IS_EMPTY_TEMPLATE; import static java.util.Objects.isNull; public class ObjectWriter { @@ -21,7 +24,9 @@ public class ObjectWriter { public void write(Service.PutRequest request) { if (isNull(request)) { - throw new IllegalArgumentException(); + throw new ProcessFrostFSException( + String.format(PROTO_MESSAGE_IS_EMPTY_TEMPLATE, Service.PutRequest.class.getName()) + ); } requestObserver.onNext(request); @@ -37,6 +42,7 @@ public class ObjectWriter { return responseObserver.getResponse(); } + @Getter private static class PutResponseCallback implements StreamObserver { private Service.PutResponse response; @@ -47,15 +53,11 @@ public class ObjectWriter { @Override public void onError(Throwable throwable) { - throw new RuntimeException(throwable); + throw new ProcessFrostFSException(throwable); } @Override public void onCompleted() { } - - public Service.PutResponse getResponse() { - return response; - } } } diff --git a/client/src/main/java/info/frostfs/sdk/tools/GrpcClient.java b/client/src/main/java/info/frostfs/sdk/tools/GrpcClient.java index 747a249..13cdfe5 100644 --- a/client/src/main/java/info/frostfs/sdk/tools/GrpcClient.java +++ b/client/src/main/java/info/frostfs/sdk/tools/GrpcClient.java @@ -1,36 +1,35 @@ package info.frostfs.sdk.tools; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; +import info.frostfs.sdk.jdo.ClientSettings; +import info.frostfs.sdk.utils.Validator; import io.grpc.Channel; -import io.grpc.ChannelCredentials; import io.grpc.netty.NettyChannelBuilder; -import org.apache.commons.lang3.StringUtils; import java.net.URI; import java.net.URISyntaxException; +import static info.frostfs.sdk.constants.ErrorConst.INVALID_HOST_TEMPLATE; import static java.util.Objects.isNull; public class GrpcClient { - private static final String ERROR_INVALID_HOST_TEMPLATE = "Host %s has invalid format. Error: %s"; - private static final String ERROR_EMPTY_HOST_MESSAGE = "Host not provided"; - private GrpcClient() { } - public static Channel initGrpcChannel(String host, ChannelCredentials credentials) { - if (StringUtils.isBlank(host)) { - throw new IllegalArgumentException(ERROR_EMPTY_HOST_MESSAGE); - } + public static Channel initGrpcChannel(ClientSettings clientSettings) { + Validator.validate(clientSettings); try { - URI uri = new URI(host); - var channelBuilder = isNull(credentials) + URI uri = new URI(clientSettings.getHost()); + var channelBuilder = isNull(clientSettings.getCredentials()) ? NettyChannelBuilder.forAddress(uri.getHost(), uri.getPort()).usePlaintext() - : NettyChannelBuilder.forAddress(uri.getHost(), uri.getPort(), credentials); + : NettyChannelBuilder.forAddress(uri.getHost(), uri.getPort(), clientSettings.getCredentials()); return channelBuilder.build(); } catch (URISyntaxException exp) { - throw new IllegalArgumentException(String.format(ERROR_INVALID_HOST_TEMPLATE, host, exp.getMessage())); + throw new ValidationFrostFSException( + String.format(INVALID_HOST_TEMPLATE, clientSettings.getHost(), exp.getMessage()) + ); } } } diff --git a/client/src/main/java/info/frostfs/sdk/tools/RequestConstructor.java b/client/src/main/java/info/frostfs/sdk/tools/RequestConstructor.java index c50c532..9ad7457 100644 --- a/client/src/main/java/info/frostfs/sdk/tools/RequestConstructor.java +++ b/client/src/main/java/info/frostfs/sdk/tools/RequestConstructor.java @@ -4,6 +4,7 @@ import com.google.protobuf.ByteString; import com.google.protobuf.Message; import frostfs.session.Types; import info.frostfs.sdk.dto.response.MetaHeader; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; import info.frostfs.sdk.jdo.ECDsa; import info.frostfs.sdk.mappers.response.MetaHeaderMapper; import org.apache.commons.collections4.MapUtils; @@ -11,6 +12,7 @@ import org.apache.commons.collections4.MapUtils; import java.util.Map; import java.util.stream.Collectors; +import static info.frostfs.sdk.constants.ErrorConst.REQUIRED_PROTO_FIELD_TEMPLATE; import static info.frostfs.sdk.constants.FieldConst.META_HEADER_FIELD_NAME; import static info.frostfs.sdk.tools.RequestSigner.signMessagePart; import static info.frostfs.sdk.utils.MessageHelper.getField; @@ -19,8 +21,6 @@ import static java.util.Objects.isNull; import static java.util.Objects.nonNull; public class RequestConstructor { - private static final String ERROR_MESSAGE = "The message does not contain a field " + META_HEADER_FIELD_NAME; - private RequestConstructor() { } @@ -40,7 +40,7 @@ public class RequestConstructor { } if (isNull(request.getDescriptorForType().findFieldByName(META_HEADER_FIELD_NAME))) { - throw new IllegalArgumentException(ERROR_MESSAGE); + throw new ValidationFrostFSException(String.format(REQUIRED_PROTO_FIELD_TEMPLATE, META_HEADER_FIELD_NAME)); } if (((Types.RequestMetaHeader) getField(request, META_HEADER_FIELD_NAME)).getSerializedSize() > 0) { diff --git a/client/src/main/java/info/frostfs/sdk/tools/RequestSigner.java b/client/src/main/java/info/frostfs/sdk/tools/RequestSigner.java index 9bc3cc2..b2ba3d4 100644 --- a/client/src/main/java/info/frostfs/sdk/tools/RequestSigner.java +++ b/client/src/main/java/info/frostfs/sdk/tools/RequestSigner.java @@ -4,6 +4,7 @@ import com.google.protobuf.ByteString; import com.google.protobuf.Message; import frostfs.session.Types; import info.frostfs.sdk.constants.CryptoConst; +import info.frostfs.sdk.exceptions.ProcessFrostFSException; import info.frostfs.sdk.jdo.ECDsa; import info.frostfs.sdk.utils.MessageHelper; import org.apache.commons.codec.digest.DigestUtils; @@ -17,15 +18,14 @@ import org.bouncycastle.crypto.signers.HMacDSAKCalculator; import java.math.BigInteger; import java.security.Signature; +import static info.frostfs.sdk.constants.CryptoConst.HASH_SIGNATURE_SIZE; +import static info.frostfs.sdk.constants.CryptoConst.RFC6979_SIGNATURE_SIZE; +import static info.frostfs.sdk.constants.ErrorConst.UNSUPPORTED_PROTO_MESSAGE_TYPE_TEMPLATE; import static info.frostfs.sdk.constants.FieldConst.*; import static org.bouncycastle.crypto.util.DigestFactory.createSHA256; import static org.bouncycastle.util.BigIntegers.asUnsignedByteArray; public class RequestSigner { - public static final String ERROR_UNSUPPORTED_TYPE_TEMPLATE = "Unsupported message type: %s"; - public static final int RFC6979_SIGNATURE_SIZE = 64; - public static final int HASH_SIGNATURE_SIZE = 65; - private RequestSigner() { } @@ -39,7 +39,7 @@ public class RequestSigner { byte[] sig = signature.sign(); System.arraycopy(sig, 0, hash, 1, sig.length); } catch (Exception exp) { - throw new RuntimeException(exp); + throw new ProcessFrostFSException(exp); } return hash; @@ -104,8 +104,8 @@ public class RequestSigner { } else if (verify instanceof Types.ResponseVerificationHeader) { verifyBuilder = Types.ResponseVerificationHeader.newBuilder(); } else { - throw new IllegalArgumentException( - String.format(ERROR_UNSUPPORTED_TYPE_TEMPLATE, verify.getClass().getName()) + throw new ProcessFrostFSException( + String.format(UNSUPPORTED_PROTO_MESSAGE_TYPE_TEMPLATE, verify.getClass().getName()) ); } diff --git a/client/src/main/java/info/frostfs/sdk/tools/Verifier.java b/client/src/main/java/info/frostfs/sdk/tools/Verifier.java index 3af7898..f4dbde3 100644 --- a/client/src/main/java/info/frostfs/sdk/tools/Verifier.java +++ b/client/src/main/java/info/frostfs/sdk/tools/Verifier.java @@ -3,7 +3,9 @@ package info.frostfs.sdk.tools; import com.google.protobuf.Message; import frostfs.session.Types; import info.frostfs.sdk.constants.CryptoConst; -import info.frostfs.sdk.exceptions.ResponseException; +import info.frostfs.sdk.exceptions.ProcessFrostFSException; +import info.frostfs.sdk.exceptions.ResponseFrostFSException; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; import info.frostfs.sdk.mappers.response.ResponseStatusMapper; import info.frostfs.sdk.utils.MessageHelper; import org.apache.commons.codec.digest.DigestUtils; @@ -20,16 +22,15 @@ import java.security.Signature; import java.util.Arrays; import static info.frostfs.sdk.KeyExtension.getPublicKeyFromBytes; +import static info.frostfs.sdk.constants.CryptoConst.RFC6979_SIGNATURE_SIZE; +import static info.frostfs.sdk.constants.ErrorConst.INVALID_RESPONSE; +import static info.frostfs.sdk.constants.ErrorConst.WRONG_SIGNATURE_SIZE_TEMPLATE; import static info.frostfs.sdk.constants.FieldConst.*; import static java.util.Objects.isNull; import static org.bouncycastle.crypto.util.DigestFactory.createSHA256; import static org.bouncycastle.util.BigIntegers.fromUnsignedByteArray; public class Verifier { - public static final String ERROR_WRONG_SIG_SIZE_TEMPLATE = "Wrong signature size. Expected length=%s, actual=%s"; - public static final String ERROR_INVALID_RESPONSE = "Invalid response"; - public static final int RFC6979_SIG_SIZE = 64; - private Verifier() { } @@ -56,28 +57,30 @@ public class Verifier { } private static BigInteger[] decodeSignature(byte[] sig) { - if (sig.length != RFC6979_SIG_SIZE) { - throw new IllegalArgumentException( - String.format(ERROR_WRONG_SIG_SIZE_TEMPLATE, RFC6979_SIG_SIZE, sig.length) + if (sig.length != RFC6979_SIGNATURE_SIZE) { + throw new ValidationFrostFSException( + String.format(WRONG_SIGNATURE_SIZE_TEMPLATE, RFC6979_SIGNATURE_SIZE, sig.length) ); } var rs = new BigInteger[2]; - rs[0] = fromUnsignedByteArray(Arrays.copyOfRange(sig, 0, (RFC6979_SIG_SIZE / 2) - 1)); - rs[1] = fromUnsignedByteArray(Arrays.copyOfRange(sig, RFC6979_SIG_SIZE / 2, RFC6979_SIG_SIZE - 1)); + rs[0] = fromUnsignedByteArray(Arrays.copyOfRange(sig, 0, (RFC6979_SIGNATURE_SIZE / 2) - 1)); + rs[1] = fromUnsignedByteArray( + Arrays.copyOfRange(sig, RFC6979_SIGNATURE_SIZE / 2, RFC6979_SIGNATURE_SIZE - 1) + ); return rs; } public static void checkResponse(Message response) { if (!verify(response)) { - throw new IllegalArgumentException(ERROR_INVALID_RESPONSE); + throw new ResponseFrostFSException(INVALID_RESPONSE); } var metaHeader = (Types.ResponseMetaHeader) MessageHelper.getField(response, META_HEADER_FIELD_NAME); var status = ResponseStatusMapper.toModel(metaHeader.getStatus()); if (!status.isSuccess()) { - throw new ResponseException(status); + throw new ResponseFrostFSException(status); } } @@ -127,7 +130,7 @@ public class Verifier { signature.update(DigestUtils.sha512(data)); return signature.verify(Arrays.copyOfRange(sig, 1, sig.length)); } catch (Exception exp) { - throw new RuntimeException(exp); + throw new ProcessFrostFSException(exp); } } } diff --git a/client/src/main/java/info/frostfs/sdk/utils/MessageHelper.java b/client/src/main/java/info/frostfs/sdk/utils/MessageHelper.java index 703db82..5c9da50 100644 --- a/client/src/main/java/info/frostfs/sdk/utils/MessageHelper.java +++ b/client/src/main/java/info/frostfs/sdk/utils/MessageHelper.java @@ -2,19 +2,19 @@ package info.frostfs.sdk.utils; import com.google.protobuf.Message; import com.google.protobuf.MessageOrBuilder; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; import org.apache.commons.lang3.StringUtils; +import static info.frostfs.sdk.constants.ErrorConst.SOME_PARAM_IS_MISSING; import static java.util.Objects.isNull; public class MessageHelper { - private static final String ERROR_MESSAGE = "One of the input parameters is null"; - private MessageHelper() { } public static Object getField(MessageOrBuilder messageOrBuilder, String fieldName) { if (isNull(messageOrBuilder) || StringUtils.isBlank(fieldName)) { - throw new IllegalArgumentException(ERROR_MESSAGE); + throw new ValidationFrostFSException(SOME_PARAM_IS_MISSING); } return messageOrBuilder.getField(messageOrBuilder.getDescriptorForType().findFieldByName(fieldName)); @@ -22,7 +22,7 @@ public class MessageHelper { public static void setField(Message.Builder builder, String fieldName, Object value) { if (isNull(builder) || StringUtils.isBlank(fieldName) || isNull(value)) { - throw new IllegalArgumentException(ERROR_MESSAGE); + throw new ValidationFrostFSException(SOME_PARAM_IS_MISSING); } builder.setField(builder.getDescriptorForType().findFieldByName(fieldName), value); diff --git a/client/src/main/java/info/frostfs/sdk/utils/Validator.java b/client/src/main/java/info/frostfs/sdk/utils/Validator.java new file mode 100644 index 0000000..f8e2120 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/utils/Validator.java @@ -0,0 +1,120 @@ +package info.frostfs.sdk.utils; + +import info.frostfs.sdk.annotations.AtLeastOneIsFilled; +import info.frostfs.sdk.annotations.NotBlank; +import info.frostfs.sdk.annotations.NotNull; +import info.frostfs.sdk.annotations.Validate; +import info.frostfs.sdk.constants.ErrorConst; +import info.frostfs.sdk.exceptions.ProcessFrostFSException; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; +import org.apache.commons.lang3.StringUtils; + +import java.lang.reflect.Field; + +import static info.frostfs.sdk.constants.ErrorConst.FIELDS_DELIMITER_COMMA; +import static java.util.Objects.isNull; + +public class Validator { + public static void validate(T object) { + StringBuilder errorMessage = new StringBuilder().append(System.lineSeparator()); + process(object, errorMessage); + + if (errorMessage.length() != 0) { + throw new ValidationFrostFSException(errorMessage.toString()); + } + } + + + private static void process(T object, StringBuilder errorMessage) { + if (isNull(object)) { + errorMessage.append(ErrorConst.OBJECT_IS_NULL); + return; + } + + Class clazz = object.getClass(); + + if (clazz.isAnnotationPresent(AtLeastOneIsFilled.class)) { + processAtLeastOneIsFilled(object, clazz, errorMessage); + } + + for (Field field : clazz.getDeclaredFields()) { + field.setAccessible(true); + + if (field.isAnnotationPresent(Validate.class)) { + processValidate(object, errorMessage, field); + } + + if (field.isAnnotationPresent(NotNull.class)) { + processNotNull(object, errorMessage, field); + } + + if (field.isAnnotationPresent(NotBlank.class)) { + processNotBlank(object, errorMessage, field); + } + } + } + + private static void processNotNull(T object, StringBuilder errorMessage, Field field) { + if (isNull(getFieldValue(object, field))) { + errorMessage + .append(String.format(ErrorConst.REQUIRED_FIELD_TEMPLATE, field.getName())) + .append(System.lineSeparator()); + } + } + + private static void processNotBlank(T object, StringBuilder errorMessage, Field field) { + var value = getFieldValue(object, field); + if (isNull(value) || (value instanceof String && StringUtils.isBlank((String) value))) { + errorMessage + .append(String.format(ErrorConst.REQUIRED_FIELD_TEMPLATE, field.getName())) + .append(System.lineSeparator()); + } + } + + private static void processValidate(T object, StringBuilder errorMessage, Field field) { + if (isNull(object)) { + return; + } + + process(getFieldValue(object, field), errorMessage); + } + + private static void processAtLeastOneIsFilled(T object, Class clazz, StringBuilder errorMessage) { + var annotation = clazz.getAnnotation(AtLeastOneIsFilled.class); + var emptyFieldsCount = 0; + for (String fieldName : annotation.fields()) { + var field = getField(clazz, fieldName); + field.setAccessible(true); + + var value = getFieldValue(object, field); + if (isNull(value) || (value instanceof String && StringUtils.isBlank((String) value))) { + emptyFieldsCount++; + } + } + + if (emptyFieldsCount == annotation.fields().length) { + errorMessage + .append(String.format( + annotation.message(), + String.join(FIELDS_DELIMITER_COMMA, annotation.fields()) + )) + .append(System.lineSeparator()); + } + } + + private static Object getFieldValue(T object, Field field) { + try { + return field.get(object); + } catch (IllegalAccessException e) { + throw new ProcessFrostFSException(e); + } + } + + private static Field getField(Class clazz, String fieldName) { + try { + return clazz.getDeclaredField(fieldName); + } catch (NoSuchFieldException e) { + throw new ProcessFrostFSException(e); + } + } +} diff --git a/cryptography/pom.xml b/cryptography/pom.xml index 385a810..33bf8fb 100644 --- a/cryptography/pom.xml +++ b/cryptography/pom.xml @@ -15,10 +15,14 @@ 11 11 UTF-8 - 3.23.0 + + info.frostfs.sdk + exceptions + 0.1.0 + com.google.protobuf protobuf-java @@ -29,11 +33,6 @@ bcprov-jdk18on 1.78.1 - - org.apache.commons - commons-lang3 - 3.14.0 - \ No newline at end of file diff --git a/cryptography/src/main/java/info/frostfs/sdk/ArrayHelper.java b/cryptography/src/main/java/info/frostfs/sdk/ArrayHelper.java index 6ccfe65..3820e62 100644 --- a/cryptography/src/main/java/info/frostfs/sdk/ArrayHelper.java +++ b/cryptography/src/main/java/info/frostfs/sdk/ArrayHelper.java @@ -1,16 +1,17 @@ package info.frostfs.sdk; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; + +import static info.frostfs.sdk.constants.ErrorConst.SOME_PARAM_IS_MISSING; import static java.util.Objects.isNull; public class ArrayHelper { - private static final String ERROR_MESSAGE = "One of the input parameters is null"; - private ArrayHelper() { } public static byte[] concat(byte[] startArray, byte[] endArray) { if (isNull(startArray) || isNull(endArray)) { - throw new IllegalArgumentException(ERROR_MESSAGE); + throw new ValidationFrostFSException(SOME_PARAM_IS_MISSING); } byte[] joinedArray = new byte[startArray.length + endArray.length]; diff --git a/cryptography/src/main/java/info/frostfs/sdk/Base58.java b/cryptography/src/main/java/info/frostfs/sdk/Base58.java index 4067687..477412b 100644 --- a/cryptography/src/main/java/info/frostfs/sdk/Base58.java +++ b/cryptography/src/main/java/info/frostfs/sdk/Base58.java @@ -1,11 +1,14 @@ package info.frostfs.sdk; +import info.frostfs.sdk.exceptions.ProcessFrostFSException; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; import org.apache.commons.lang3.StringUtils; import java.util.Arrays; import static info.frostfs.sdk.ArrayHelper.concat; import static info.frostfs.sdk.Helper.getSha256; +import static info.frostfs.sdk.constants.ErrorConst.*; import static java.util.Objects.isNull; public class Base58 { @@ -16,8 +19,6 @@ public class Base58 { private static final char ENCODED_ZERO = ALPHABET[0]; private static final char BASE58_ASCII_MAX_VALUE = 128; private static final int[] INDEXES = new int[BASE58_ASCII_MAX_VALUE]; - private static final String ERROR_VALUE_MISSING_MESSAGE = "Input value is missing"; - private static final String ERROR_INVALID_CHARACTER_TEMPLATE = "Invalid character in Base58: 0x%04x"; static { Arrays.fill(INDEXES, -1); @@ -31,12 +32,12 @@ public class Base58 { public static byte[] base58CheckDecode(String input) { if (StringUtils.isEmpty(input)) { - throw new IllegalArgumentException(ERROR_VALUE_MISSING_MESSAGE); + throw new ValidationFrostFSException(INPUT_PARAM_IS_MISSING); } byte[] buffer = decode(input); if (buffer.length < 4) { - throw new IllegalArgumentException(); + throw new ProcessFrostFSException(String.format(DECODE_LENGTH_VALUE_TEMPLATE, buffer.length)); } byte[] decode = Arrays.copyOfRange(buffer, 0, buffer.length - 4); @@ -44,7 +45,7 @@ public class Base58 { var bufferEnd = Arrays.copyOfRange(buffer, buffer.length - 4, buffer.length); var checksumStart = Arrays.copyOfRange(checksum, 0, 4); if (!Arrays.equals(bufferEnd, checksumStart)) { - throw new IllegalArgumentException(); + throw new ProcessFrostFSException(INVALID_CHECKSUM); } return decode; @@ -52,7 +53,7 @@ public class Base58 { public static String base58CheckEncode(byte[] data) { if (isNull(data)) { - throw new IllegalArgumentException(ERROR_VALUE_MISSING_MESSAGE); + throw new ValidationFrostFSException(INPUT_PARAM_IS_MISSING); } byte[] checksum = getSha256(getSha256(data)); @@ -101,7 +102,7 @@ public class Base58 { char c = input.charAt(i); int digit = c < BASE58_ASCII_MAX_VALUE ? INDEXES[c] : -1; if (digit < 0) { - throw new IllegalArgumentException(String.format(ERROR_INVALID_CHARACTER_TEMPLATE, (int) c)); + throw new ValidationFrostFSException(String.format(INVALID_BASE58_CHARACTER_TEMPLATE, (int) c)); } input58[i] = (byte) digit; } diff --git a/cryptography/src/main/java/info/frostfs/sdk/Helper.java b/cryptography/src/main/java/info/frostfs/sdk/Helper.java index 529da35..29e7fdc 100644 --- a/cryptography/src/main/java/info/frostfs/sdk/Helper.java +++ b/cryptography/src/main/java/info/frostfs/sdk/Helper.java @@ -2,16 +2,17 @@ package info.frostfs.sdk; import com.google.protobuf.ByteString; import com.google.protobuf.Message; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; import org.bouncycastle.crypto.digests.RIPEMD160Digest; import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import static info.frostfs.sdk.constants.ErrorConst.INPUT_PARAM_IS_MISSING; import static java.util.Objects.isNull; public class Helper { - private static final String ERROR_MESSAGE = "Input value is missing"; private static final String SHA256 = "SHA-256"; private static final int RIPEMD_160_HASH_BYTE_LENGTH = 20; @@ -20,7 +21,7 @@ public class Helper { public static byte[] getRipemd160(byte[] value) { if (isNull(value)) { - throw new IllegalArgumentException(ERROR_MESSAGE); + throw new ValidationFrostFSException(INPUT_PARAM_IS_MISSING); } var hash = new byte[RIPEMD_160_HASH_BYTE_LENGTH]; @@ -40,7 +41,7 @@ public class Helper { public static byte[] getSha256(byte[] value) { if (isNull(value)) { - throw new IllegalArgumentException(ERROR_MESSAGE); + throw new ValidationFrostFSException(INPUT_PARAM_IS_MISSING); } return getSha256Instance().digest(value); @@ -48,7 +49,7 @@ public class Helper { public static ByteString getSha256(Message value) { if (isNull(value)) { - throw new IllegalArgumentException(ERROR_MESSAGE); + throw new ValidationFrostFSException(INPUT_PARAM_IS_MISSING); } return ByteString.copyFrom(getSha256(value.toByteArray())); @@ -56,7 +57,7 @@ public class Helper { public static String getHexString(byte[] value) { if (isNull(value) || value.length == 0) { - throw new IllegalArgumentException(ERROR_MESSAGE); + throw new ValidationFrostFSException(INPUT_PARAM_IS_MISSING); } return String.format("%0" + (value.length << 1) + "x", new BigInteger(1, value)); diff --git a/cryptography/src/main/java/info/frostfs/sdk/KeyExtension.java b/cryptography/src/main/java/info/frostfs/sdk/KeyExtension.java index 25ce3af..f939c08 100644 --- a/cryptography/src/main/java/info/frostfs/sdk/KeyExtension.java +++ b/cryptography/src/main/java/info/frostfs/sdk/KeyExtension.java @@ -1,5 +1,7 @@ package info.frostfs.sdk; +import info.frostfs.sdk.exceptions.ProcessFrostFSException; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; import org.apache.commons.lang3.StringUtils; import org.bouncycastle.asn1.sec.SECNamedCurves; import org.bouncycastle.asn1.sec.SECObjectIdentifiers; @@ -26,6 +28,7 @@ import java.util.Arrays; import static info.frostfs.sdk.Helper.getRipemd160; import static info.frostfs.sdk.Helper.getSha256; +import static info.frostfs.sdk.constants.ErrorConst.*; import static java.util.Objects.isNull; import static org.bouncycastle.util.BigIntegers.fromUnsignedByteArray; @@ -40,13 +43,6 @@ public class KeyExtension { private static final int CHECK_SIG_DESCRIPTOR = ByteBuffer .wrap(getSha256("System.Crypto.CheckSig".getBytes(StandardCharsets.US_ASCII))) .order(ByteOrder.LITTLE_ENDIAN).getInt(); - private static final String ERROR_VALUE_MISSING_MESSAGE = "Input value is missing"; - private static final String ERROR_ENCODED_COMPRESSED_PUBLIC_KEY_TEMPLATE = - "PublicKey isn't encoded compressed public key. Expected length=%s, actual=%s"; - private static final String ERROR_UNCOMPRESSED_PUBLIC_KEY_TEMPLATE = - "Compress argument isn't uncompressed public key. Expected length=%s, actual=%s"; - private static final String ERROR_COMPRESSED_PUBLIC_KEY_TEMPLATE = - "Decompress argument isn't compressed public key. Expected length=%s, actual=%s"; private KeyExtension() { } @@ -54,8 +50,8 @@ public class KeyExtension { public static byte[] compress(byte[] publicKey) { checkInputValue(publicKey); if (publicKey.length != UNCOMPRESSED_PUBLIC_KEY_LENGTH) { - throw new IllegalArgumentException(String.format( - ERROR_UNCOMPRESSED_PUBLIC_KEY_TEMPLATE, UNCOMPRESSED_PUBLIC_KEY_LENGTH, publicKey.length + throw new ValidationFrostFSException(String.format( + UNCOMPRESSED_PUBLIC_KEY_WRONG_LENGTH_TEMPLATE, UNCOMPRESSED_PUBLIC_KEY_LENGTH, publicKey.length )); } @@ -66,7 +62,7 @@ public class KeyExtension { public static byte[] getPrivateKeyFromWIF(String wif) { if (StringUtils.isEmpty(wif)) { - throw new IllegalArgumentException(ERROR_VALUE_MISSING_MESSAGE); + throw new ValidationFrostFSException(INPUT_PARAM_IS_MISSING); } var data = Base58.base58CheckDecode(wif); @@ -102,7 +98,7 @@ public class KeyExtension { KeyFactory kf = KeyFactory.getInstance(SECURITY_ALGORITHM); return kf.generatePrivate(privateKeySpec); } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { - throw new RuntimeException(e); + throw new ProcessFrostFSException(e); } } @@ -110,9 +106,9 @@ public class KeyExtension { checkInputValue(publicKey); if (publicKey.length != COMPRESSED_PUBLIC_KEY_LENGTH) { - throw new IllegalArgumentException( - String.format(ERROR_COMPRESSED_PUBLIC_KEY_TEMPLATE, COMPRESSED_PUBLIC_KEY_LENGTH, publicKey.length) - ); + throw new ValidationFrostFSException(String.format( + COMPRESSED_PUBLIC_KEY_WRONG_LENGTH_TEMPLATE, COMPRESSED_PUBLIC_KEY_LENGTH, publicKey.length + )); } X9ECParameters secp256R1 = SECNamedCurves.getByOID(SECObjectIdentifiers.secp256r1); @@ -134,7 +130,7 @@ public class KeyExtension { KeyFactory kf = KeyFactory.getInstance(SECURITY_ALGORITHM); return kf.generatePublic(publicKeySpec); } catch (Exception e) { - throw new RuntimeException(e); + throw new ProcessFrostFSException(e); } } @@ -148,8 +144,8 @@ public class KeyExtension { public static String publicKeyToAddress(byte[] publicKey) { checkInputValue(publicKey); if (publicKey.length != COMPRESSED_PUBLIC_KEY_LENGTH) { - throw new IllegalArgumentException(String.format( - ERROR_ENCODED_COMPRESSED_PUBLIC_KEY_TEMPLATE, COMPRESSED_PUBLIC_KEY_LENGTH, publicKey.length + throw new ValidationFrostFSException(String.format( + ENCODED_COMPRESSED_PUBLIC_KEY_WRONG_LENGTH_TEMPLATE, COMPRESSED_PUBLIC_KEY_LENGTH, publicKey.length )); } @@ -177,8 +173,8 @@ public class KeyExtension { private static byte[] createSignatureRedeemScript(byte[] publicKey) { checkInputValue(publicKey); if (publicKey.length != COMPRESSED_PUBLIC_KEY_LENGTH) { - throw new IllegalArgumentException(String.format( - ERROR_ENCODED_COMPRESSED_PUBLIC_KEY_TEMPLATE, COMPRESSED_PUBLIC_KEY_LENGTH, publicKey.length + throw new ValidationFrostFSException(String.format( + ENCODED_COMPRESSED_PUBLIC_KEY_WRONG_LENGTH_TEMPLATE, COMPRESSED_PUBLIC_KEY_LENGTH, publicKey.length )); } @@ -192,7 +188,7 @@ public class KeyExtension { private static void checkInputValue(byte[] data) { if (isNull(data) || data.length == 0) { - throw new IllegalArgumentException(ERROR_VALUE_MISSING_MESSAGE); + throw new ValidationFrostFSException(INPUT_PARAM_IS_MISSING); } } } diff --git a/cryptography/src/test/java/info/frostfs/sdk/ArrayHelperTest.java b/cryptography/src/test/java/info/frostfs/sdk/ArrayHelperTest.java index ab8a1e1..0d11b0e 100644 --- a/cryptography/src/test/java/info/frostfs/sdk/ArrayHelperTest.java +++ b/cryptography/src/test/java/info/frostfs/sdk/ArrayHelperTest.java @@ -1,5 +1,6 @@ package info.frostfs.sdk; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -59,8 +60,8 @@ public class ArrayHelperTest { var endBytes = new byte[]{6, 7, 8, 9}; //When + Then - assertThrows(IllegalArgumentException.class, () -> ArrayHelper.concat(startBytes, null)); - assertThrows(IllegalArgumentException.class, () -> ArrayHelper.concat(null, endBytes)); - assertThrows(IllegalArgumentException.class, () -> ArrayHelper.concat(null, null)); + assertThrows(ValidationFrostFSException.class, () -> ArrayHelper.concat(startBytes, null)); + assertThrows(ValidationFrostFSException.class, () -> ArrayHelper.concat(null, endBytes)); + assertThrows(ValidationFrostFSException.class, () -> ArrayHelper.concat(null, null)); } } diff --git a/cryptography/src/test/java/info/frostfs/sdk/Base58Test.java b/cryptography/src/test/java/info/frostfs/sdk/Base58Test.java index 7ac3d9d..bebcae4 100644 --- a/cryptography/src/test/java/info/frostfs/sdk/Base58Test.java +++ b/cryptography/src/test/java/info/frostfs/sdk/Base58Test.java @@ -1,5 +1,7 @@ package info.frostfs.sdk; +import info.frostfs.sdk.exceptions.ProcessFrostFSException; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -31,10 +33,10 @@ public class Base58Test { @Test void base58Decode_wrong() { //When + Then - assertThrows(IllegalArgumentException.class, () -> Base58.base58CheckDecode(null)); - assertThrows(IllegalArgumentException.class, () -> Base58.base58CheckDecode("")); - assertThrows(IllegalArgumentException.class, () -> Base58.base58CheckDecode("WIF")); - assertThrows(IllegalArgumentException.class, () -> Base58.base58CheckDecode("fh")); + assertThrows(ValidationFrostFSException.class, () -> Base58.base58CheckDecode(null)); + assertThrows(ValidationFrostFSException.class, () -> Base58.base58CheckDecode("")); + assertThrows(ValidationFrostFSException.class, () -> Base58.base58CheckDecode("WIF")); + assertThrows(ProcessFrostFSException.class, () -> Base58.base58CheckDecode("fh")); } @Test @@ -49,6 +51,6 @@ public class Base58Test { @Test void base58Encode_wrong() { //When + Then - assertThrows(IllegalArgumentException.class, () -> Base58.base58CheckEncode(null)); + assertThrows(ValidationFrostFSException.class, () -> Base58.base58CheckEncode(null)); } } diff --git a/cryptography/src/test/java/info/frostfs/sdk/HelperTest.java b/cryptography/src/test/java/info/frostfs/sdk/HelperTest.java index 67cfaba..cb1e854 100644 --- a/cryptography/src/test/java/info/frostfs/sdk/HelperTest.java +++ b/cryptography/src/test/java/info/frostfs/sdk/HelperTest.java @@ -1,6 +1,7 @@ package info.frostfs.sdk; import com.google.protobuf.Message; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -25,7 +26,7 @@ public class HelperTest { @Test void getRipemd160_givenParamIsNull() { //When + Then - assertThrows(IllegalArgumentException.class, () -> Helper.getRipemd160(null)); + assertThrows(ValidationFrostFSException.class, () -> Helper.getRipemd160(null)); } @Test @@ -69,8 +70,8 @@ public class HelperTest { @Test void getSha256_bytes_givenParamIsNull() { //When + Then - assertThrows(IllegalArgumentException.class, () -> Helper.getSha256((byte[]) null)); - assertThrows(IllegalArgumentException.class, () -> Helper.getSha256((Message) null)); + assertThrows(ValidationFrostFSException.class, () -> Helper.getSha256((byte[]) null)); + assertThrows(ValidationFrostFSException.class, () -> Helper.getSha256((Message) null)); } @Test @@ -104,7 +105,7 @@ public class HelperTest { @Test void getHexString_wrong() { //When + Then - assertThrows(IllegalArgumentException.class, () -> Helper.getHexString(null)); - assertThrows(IllegalArgumentException.class, () -> Helper.getHexString(new byte[]{})); + assertThrows(ValidationFrostFSException.class, () -> Helper.getHexString(null)); + assertThrows(ValidationFrostFSException.class, () -> Helper.getHexString(new byte[]{})); } } diff --git a/cryptography/src/test/java/info/frostfs/sdk/KeyExtensionTest.java b/cryptography/src/test/java/info/frostfs/sdk/KeyExtensionTest.java index c54025a..2f9b1b9 100644 --- a/cryptography/src/test/java/info/frostfs/sdk/KeyExtensionTest.java +++ b/cryptography/src/test/java/info/frostfs/sdk/KeyExtensionTest.java @@ -1,5 +1,6 @@ package info.frostfs.sdk; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -35,8 +36,8 @@ public class KeyExtensionTest { @Test void getPrivateKeyFromWIF_wrong() { //When + Then - assertThrows(IllegalArgumentException.class, () -> KeyExtension.getPrivateKeyFromWIF("")); - assertThrows(IllegalArgumentException.class, () -> KeyExtension.getPrivateKeyFromWIF(null)); + assertThrows(ValidationFrostFSException.class, () -> KeyExtension.getPrivateKeyFromWIF("")); + assertThrows(ValidationFrostFSException.class, () -> KeyExtension.getPrivateKeyFromWIF(null)); } @Test @@ -51,8 +52,8 @@ public class KeyExtensionTest { @Test void loadPublicKey_wrong() { //When + Then - assertThrows(IllegalArgumentException.class, () -> KeyExtension.loadPublicKey(null)); - assertThrows(IllegalArgumentException.class, () -> KeyExtension.loadPublicKey(new byte[]{})); + assertThrows(ValidationFrostFSException.class, () -> KeyExtension.loadPublicKey(null)); + assertThrows(ValidationFrostFSException.class, () -> KeyExtension.loadPublicKey(new byte[]{})); } @Test @@ -69,8 +70,8 @@ public class KeyExtensionTest { @Test void loadPrivateKey_wrong() { //When + Then - assertThrows(IllegalArgumentException.class, () -> KeyExtension.loadPrivateKey(null)); - assertThrows(IllegalArgumentException.class, () -> KeyExtension.loadPrivateKey(new byte[]{})); + assertThrows(ValidationFrostFSException.class, () -> KeyExtension.loadPrivateKey(null)); + assertThrows(ValidationFrostFSException.class, () -> KeyExtension.loadPrivateKey(new byte[]{})); } @Test @@ -87,9 +88,9 @@ public class KeyExtensionTest { @Test void getPublicKeyFromBytes_wrong() { //When + Then - assertThrows(IllegalArgumentException.class, () -> KeyExtension.getPublicKeyFromBytes(null)); - assertThrows(IllegalArgumentException.class, () -> KeyExtension.getPublicKeyFromBytes(new byte[]{})); - assertThrows(IllegalArgumentException.class, () -> KeyExtension.getPublicKeyFromBytes(PRIVATE_KEY)); + assertThrows(ValidationFrostFSException.class, () -> KeyExtension.getPublicKeyFromBytes(null)); + assertThrows(ValidationFrostFSException.class, () -> KeyExtension.getPublicKeyFromBytes(new byte[]{})); + assertThrows(ValidationFrostFSException.class, () -> KeyExtension.getPublicKeyFromBytes(PRIVATE_KEY)); } @Test @@ -109,9 +110,9 @@ public class KeyExtensionTest { @Test void getScriptHash_wrong() { //When + Then - assertThrows(IllegalArgumentException.class, () -> KeyExtension.getScriptHash(null)); - assertThrows(IllegalArgumentException.class, () -> KeyExtension.getScriptHash(new byte[]{})); - assertThrows(IllegalArgumentException.class, () -> KeyExtension.getScriptHash(PRIVATE_KEY)); + assertThrows(ValidationFrostFSException.class, () -> KeyExtension.getScriptHash(null)); + assertThrows(ValidationFrostFSException.class, () -> KeyExtension.getScriptHash(new byte[]{})); + assertThrows(ValidationFrostFSException.class, () -> KeyExtension.getScriptHash(PRIVATE_KEY)); } @Test @@ -126,9 +127,9 @@ public class KeyExtensionTest { @Test void publicKeyToAddress_wrong() { //When + Then - assertThrows(IllegalArgumentException.class, () -> KeyExtension.publicKeyToAddress(null)); - assertThrows(IllegalArgumentException.class, () -> KeyExtension.publicKeyToAddress(new byte[]{})); - assertThrows(IllegalArgumentException.class, () -> KeyExtension.publicKeyToAddress(PRIVATE_KEY)); + assertThrows(ValidationFrostFSException.class, () -> KeyExtension.publicKeyToAddress(null)); + assertThrows(ValidationFrostFSException.class, () -> KeyExtension.publicKeyToAddress(new byte[]{})); + assertThrows(ValidationFrostFSException.class, () -> KeyExtension.publicKeyToAddress(PRIVATE_KEY)); } @Test @@ -143,9 +144,9 @@ public class KeyExtensionTest { @Test void compress_wrong() { //When + Then - assertThrows(IllegalArgumentException.class, () -> KeyExtension.compress(null)); - assertThrows(IllegalArgumentException.class, () -> KeyExtension.compress(new byte[]{})); - assertThrows(IllegalArgumentException.class, () -> KeyExtension.compress(PUBLIC_KEY)); + assertThrows(ValidationFrostFSException.class, () -> KeyExtension.compress(null)); + assertThrows(ValidationFrostFSException.class, () -> KeyExtension.compress(new byte[]{})); + assertThrows(ValidationFrostFSException.class, () -> KeyExtension.compress(PUBLIC_KEY)); } } diff --git a/exceptions/pom.xml b/exceptions/pom.xml new file mode 100644 index 0000000..2c80de2 --- /dev/null +++ b/exceptions/pom.xml @@ -0,0 +1,20 @@ + + + 4.0.0 + + info.frostfs.sdk + frostfs-sdk-java + 0.1.0 + + + exceptions + + + 11 + 11 + UTF-8 + + + \ No newline at end of file diff --git a/exceptions/src/main/java/info/frostfs/sdk/constants/ErrorConst.java b/exceptions/src/main/java/info/frostfs/sdk/constants/ErrorConst.java new file mode 100644 index 0000000..1bf25d4 --- /dev/null +++ b/exceptions/src/main/java/info/frostfs/sdk/constants/ErrorConst.java @@ -0,0 +1,49 @@ +package info.frostfs.sdk.constants; + +public class ErrorConst { + public static final String OBJECT_IS_NULL = "object must not be null"; + public static final String INPUT_PARAM_IS_MISSING = "input parameter is not present"; + public static final String SOME_PARAM_IS_MISSING = "one of the input parameters is not present"; + public static final String PARAM_IS_MISSING_TEMPLATE = "param %s is not present"; + public static final String PARAMS_ARE_MISSING_TEMPLATE = "params %s are not present"; + public static final String INPUT_PARAM_IS_MISSING_TEMPLATE = "%s value is not present"; + + public static final String INPUT_PARAM_GREATER_OR_EQUAL_ZERO = "%s must be greater than or equal to zero"; + public static final String INPUT_PARAM_GREATER_ZERO = "%s must be greater than zero"; + + public static final String REQUIRED_FIELD_TEMPLATE = "%s required field"; + + public static final String REQUIRED_PROTO_FIELD_TEMPLATE = "the proto message does not contain a field %s"; + public static final String UNSUPPORTED_PROTO_MESSAGE_TYPE_TEMPLATE = "unsupported proto message type: %s"; + public static final String PROTO_MESSAGE_IS_EMPTY_TEMPLATE = "proto message %s must not be empty"; + public static final String UNEXPECTED_MESSAGE_TYPE_TEMPLATE = "unexpected message type, expected %s, actually %s"; + + public static final String WIF_IS_INVALID = "WIF is invalid"; + public static final String UNEXPECTED_STREAM = "unexpected end of stream"; + public static final String INVALID_HOST_TEMPLATE = "host %s has invalid format. Error: %s"; + public static final String INVALID_RESPONSE = "invalid response"; + public static final String VERSION_UNSUPPORTED_TEMPLATE = "frostFS %s is not supported."; + + public static final String UNKNOWN_ENUM_VALUE_TEMPLATE = "unknown %s value: %s"; + + public static final String INPUT_PARAM_IS_NOT_SHA256 = "%s must be a sha256 hash"; + public static final String DECODE_LENGTH_VALUE_TEMPLATE = "decode array length must be >= 4, but %s"; + public static final String INVALID_BASE58_CHARACTER_TEMPLATE = "invalid character in Base58: 0x%04x"; + public static final String INVALID_CHECKSUM = "checksum does not match"; + public static final String WRONG_SIGNATURE_SIZE_TEMPLATE = "wrong signature size. Expected length=%s, actual=%s"; + public static final String ENCODED_COMPRESSED_PUBLIC_KEY_WRONG_LENGTH_TEMPLATE = + "publicKey isn't encoded compressed public key. Expected length=%s, actual=%s"; + public static final String UNCOMPRESSED_PUBLIC_KEY_WRONG_LENGTH_TEMPLATE = + "compress argument isn't uncompressed public key. Expected length=%s, actual=%s"; + public static final String COMPRESSED_PUBLIC_KEY_WRONG_LENGTH_TEMPLATE = + "decompress argument isn't compressed public key. Expected length=%s, actual=%s"; + + public static final String WRONG_UUID_SIZE_TEMPLATE = "uuid byte array length must be %s"; + + + public static final String FIELDS_DELIMITER_COMMA = ", "; + public static final String FIELDS_DELIMITER_OR = " or "; + + private ErrorConst() { + } +} diff --git a/exceptions/src/main/java/info/frostfs/sdk/exceptions/ProcessFrostFSException.java b/exceptions/src/main/java/info/frostfs/sdk/exceptions/ProcessFrostFSException.java new file mode 100644 index 0000000..47e06b3 --- /dev/null +++ b/exceptions/src/main/java/info/frostfs/sdk/exceptions/ProcessFrostFSException.java @@ -0,0 +1,11 @@ +package info.frostfs.sdk.exceptions; + +public class ProcessFrostFSException extends RuntimeException { + public ProcessFrostFSException(String message) { + super(message); + } + + public ProcessFrostFSException(Throwable cause) { + super(cause); + } +} diff --git a/exceptions/src/main/java/info/frostfs/sdk/exceptions/TimeoutFrostFSException.java b/exceptions/src/main/java/info/frostfs/sdk/exceptions/TimeoutFrostFSException.java new file mode 100644 index 0000000..fca9263 --- /dev/null +++ b/exceptions/src/main/java/info/frostfs/sdk/exceptions/TimeoutFrostFSException.java @@ -0,0 +1,6 @@ +package info.frostfs.sdk.exceptions; + +public class TimeoutFrostFSException extends RuntimeException { + public TimeoutFrostFSException() { + } +} diff --git a/exceptions/src/main/java/info/frostfs/sdk/exceptions/ValidationFrostFSException.java b/exceptions/src/main/java/info/frostfs/sdk/exceptions/ValidationFrostFSException.java new file mode 100644 index 0000000..d294ef8 --- /dev/null +++ b/exceptions/src/main/java/info/frostfs/sdk/exceptions/ValidationFrostFSException.java @@ -0,0 +1,7 @@ +package info.frostfs.sdk.exceptions; + +public class ValidationFrostFSException extends RuntimeException { + public ValidationFrostFSException(String message) { + super(message); + } +} diff --git a/models/pom.xml b/models/pom.xml index edc1c26..a5dfe24 100644 --- a/models/pom.xml +++ b/models/pom.xml @@ -29,14 +29,9 @@ 0.1.0 - org.apache.commons - commons-lang3 - 3.14.0 - - - org.apache.commons - commons-collections4 - 4.4 + info.frostfs.sdk + exceptions + 0.1.0 diff --git a/models/src/main/java/info/frostfs/sdk/UuidExtension.java b/models/src/main/java/info/frostfs/sdk/UuidExtension.java index 9cec1c2..5060baa 100644 --- a/models/src/main/java/info/frostfs/sdk/UuidExtension.java +++ b/models/src/main/java/info/frostfs/sdk/UuidExtension.java @@ -1,21 +1,22 @@ package info.frostfs.sdk; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; + import java.nio.ByteBuffer; import java.util.UUID; +import static info.frostfs.sdk.constants.AppConst.UUID_BYTE_ARRAY_LENGTH; +import static info.frostfs.sdk.constants.ErrorConst.INPUT_PARAM_IS_MISSING_TEMPLATE; +import static info.frostfs.sdk.constants.ErrorConst.WRONG_UUID_SIZE_TEMPLATE; import static java.util.Objects.isNull; public class UuidExtension { - private static final int UUID_BYTE_ARRAY_LENGTH = 16; - private static final String ERROR_WRONG_UUID_SIZE = "Uuid byte array length must be " + UUID_BYTE_ARRAY_LENGTH; - private static final String ERROR_UUID_MISSING = "Uuid is not present"; - private UuidExtension() { } public static UUID asUuid(byte[] bytes) { if (isNull(bytes) || bytes.length != UUID_BYTE_ARRAY_LENGTH) { - throw new IllegalArgumentException(ERROR_WRONG_UUID_SIZE); + throw new ValidationFrostFSException(String.format(WRONG_UUID_SIZE_TEMPLATE, UUID_BYTE_ARRAY_LENGTH)); } ByteBuffer bb = ByteBuffer.wrap(bytes); @@ -26,7 +27,9 @@ public class UuidExtension { public static byte[] asBytes(UUID uuid) { if (isNull(uuid)) { - throw new IllegalArgumentException(ERROR_UUID_MISSING); + throw new ValidationFrostFSException( + String.format(INPUT_PARAM_IS_MISSING_TEMPLATE, UUID.class.getName()) + ); } ByteBuffer bb = ByteBuffer.allocate(UUID_BYTE_ARRAY_LENGTH); diff --git a/models/src/main/java/info/frostfs/sdk/constants/AppConst.java b/models/src/main/java/info/frostfs/sdk/constants/AppConst.java index d535ee7..9942aa7 100644 --- a/models/src/main/java/info/frostfs/sdk/constants/AppConst.java +++ b/models/src/main/java/info/frostfs/sdk/constants/AppConst.java @@ -9,6 +9,7 @@ public class AppConst { public static final int MIB = KIB << BYTE_SHIFT; public static final int OBJECT_CHUNK_SIZE = 3 * MIB; public static final int SHA256_HASH_LENGTH = 32; + public static final int UUID_BYTE_ARRAY_LENGTH = 16; private AppConst() { } diff --git a/models/src/main/java/info/frostfs/sdk/dto/CheckSum.java b/models/src/main/java/info/frostfs/sdk/dto/CheckSum.java index ad298c4..c414572 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/CheckSum.java +++ b/models/src/main/java/info/frostfs/sdk/dto/CheckSum.java @@ -1,7 +1,11 @@ package info.frostfs.sdk.dto; import info.frostfs.sdk.Helper; +import lombok.Getter; +import lombok.Setter; +@Getter +@Setter public class CheckSum { // type is always Sha256 public byte[] hash; @@ -10,14 +14,6 @@ public class CheckSum { this.hash = Helper.getSha256(content); } - public byte[] getHash() { - return hash; - } - - public void setHash(byte[] hash) { - this.hash = hash; - } - @Override public String toString() { return Helper.getHexString(hash); diff --git a/models/src/main/java/info/frostfs/sdk/dto/container/Container.java b/models/src/main/java/info/frostfs/sdk/dto/container/Container.java index 174d487..2d0816e 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/container/Container.java +++ b/models/src/main/java/info/frostfs/sdk/dto/container/Container.java @@ -3,9 +3,13 @@ package info.frostfs.sdk.dto.container; import info.frostfs.sdk.dto.netmap.PlacementPolicy; import info.frostfs.sdk.dto.netmap.Version; import info.frostfs.sdk.enums.BasicAcl; +import lombok.Getter; +import lombok.Setter; import java.util.UUID; +@Getter +@Setter public class Container { private UUID nonce; private BasicAcl basicAcl; @@ -17,36 +21,4 @@ public class Container { this.basicAcl = basicAcl; this.placementPolicy = placementPolicy; } - - public UUID getNonce() { - return nonce; - } - - public void setNonce(UUID nonce) { - this.nonce = nonce; - } - - public BasicAcl getBasicAcl() { - return basicAcl; - } - - public void setBasicAcl(BasicAcl basicAcl) { - this.basicAcl = basicAcl; - } - - public PlacementPolicy getPlacementPolicy() { - return placementPolicy; - } - - public void setPlacementPolicy(PlacementPolicy placementPolicy) { - this.placementPolicy = placementPolicy; - } - - public Version getVersion() { - return version; - } - - public void setVersion(Version version) { - this.version = version; - } } diff --git a/models/src/main/java/info/frostfs/sdk/dto/container/ContainerId.java b/models/src/main/java/info/frostfs/sdk/dto/container/ContainerId.java index 1cd39b2..393e3d0 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/container/ContainerId.java +++ b/models/src/main/java/info/frostfs/sdk/dto/container/ContainerId.java @@ -2,16 +2,24 @@ package info.frostfs.sdk.dto.container; import info.frostfs.sdk.Base58; import info.frostfs.sdk.constants.AppConst; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; +import lombok.Getter; import org.apache.commons.lang3.StringUtils; +import static info.frostfs.sdk.constants.ErrorConst.INPUT_PARAM_IS_MISSING_TEMPLATE; +import static info.frostfs.sdk.constants.ErrorConst.INPUT_PARAM_IS_NOT_SHA256; import static java.util.Objects.isNull; +@Getter public class ContainerId { + private final String value; public ContainerId(String value) { if (StringUtils.isEmpty(value)) { - throw new IllegalArgumentException("ContainerId value is missing"); + throw new ValidationFrostFSException( + String.format(INPUT_PARAM_IS_MISSING_TEMPLATE, ContainerId.class.getName()) + ); } this.value = value; @@ -19,16 +27,14 @@ public class ContainerId { public ContainerId(byte[] hash) { if (isNull(hash) || hash.length != AppConst.SHA256_HASH_LENGTH) { - throw new IllegalArgumentException("ContainerId must be a sha256 hash."); + throw new ValidationFrostFSException( + String.format(INPUT_PARAM_IS_NOT_SHA256, ContainerId.class.getName()) + ); } this.value = Base58.encode(hash); } - public String getValue() { - return value; - } - @Override public String toString() { return value; diff --git a/models/src/main/java/info/frostfs/sdk/dto/netmap/NetmapSnapshot.java b/models/src/main/java/info/frostfs/sdk/dto/netmap/NetmapSnapshot.java index 69670bb..6cde815 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/netmap/NetmapSnapshot.java +++ b/models/src/main/java/info/frostfs/sdk/dto/netmap/NetmapSnapshot.java @@ -1,22 +1,13 @@ package info.frostfs.sdk.dto.netmap; -import java.util.Collections; +import lombok.AllArgsConstructor; +import lombok.Getter; + import java.util.List; +@Getter +@AllArgsConstructor public class NetmapSnapshot { private final Long epoch; private final List nodeInfoCollection; - - public NetmapSnapshot(Long epoch, List nodeInfoCollection) { - this.epoch = epoch; - this.nodeInfoCollection = Collections.unmodifiableList(nodeInfoCollection); - } - - public Long getEpoch() { - return epoch; - } - - public List getNodeInfoCollection() { - return nodeInfoCollection; - } } diff --git a/models/src/main/java/info/frostfs/sdk/dto/netmap/NodeInfo.java b/models/src/main/java/info/frostfs/sdk/dto/netmap/NodeInfo.java index 75c2484..a3d28f8 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/netmap/NodeInfo.java +++ b/models/src/main/java/info/frostfs/sdk/dto/netmap/NodeInfo.java @@ -1,11 +1,13 @@ package info.frostfs.sdk.dto.netmap; import info.frostfs.sdk.enums.NodeState; +import lombok.Getter; import java.util.Collections; import java.util.List; import java.util.Map; +@Getter public class NodeInfo { private final NodeState state; private final Version version; @@ -21,24 +23,4 @@ public class NodeInfo { this.attributes = Collections.unmodifiableMap(attributes); this.publicKey = publicKey; } - - public NodeState getState() { - return state; - } - - public Version getVersion() { - return version; - } - - public byte[] getPublicKey() { - return publicKey; - } - - public Map getAttributes() { - return attributes; - } - - public List getAddresses() { - return addresses; - } } diff --git a/models/src/main/java/info/frostfs/sdk/dto/netmap/PlacementPolicy.java b/models/src/main/java/info/frostfs/sdk/dto/netmap/PlacementPolicy.java index 74a1c72..0356dea 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/netmap/PlacementPolicy.java +++ b/models/src/main/java/info/frostfs/sdk/dto/netmap/PlacementPolicy.java @@ -1,19 +1,11 @@ package info.frostfs.sdk.dto.netmap; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor public class PlacementPolicy { private final Replica[] replicas; private final boolean unique; - - public PlacementPolicy(boolean unique, Replica[] replicas) { - this.replicas = replicas; - this.unique = unique; - } - - public Replica[] getReplicas() { - return replicas; - } - - public boolean isUnique() { - return unique; - } } diff --git a/models/src/main/java/info/frostfs/sdk/dto/netmap/Replica.java b/models/src/main/java/info/frostfs/sdk/dto/netmap/Replica.java index 74e3886..5c28462 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/netmap/Replica.java +++ b/models/src/main/java/info/frostfs/sdk/dto/netmap/Replica.java @@ -1,16 +1,24 @@ package info.frostfs.sdk.dto.netmap; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; +import lombok.Getter; +import lombok.experimental.FieldNameConstants; import org.apache.commons.lang3.StringUtils; +import static info.frostfs.sdk.constants.ErrorConst.INPUT_PARAM_GREATER_ZERO; import static info.frostfs.sdk.constants.FieldConst.EMPTY_STRING; +@Getter +@FieldNameConstants public class Replica { private final int count; private final String selector; public Replica(int count, String selector) { if (count <= 0) { - throw new IllegalArgumentException("Replica count must be positive"); + throw new ValidationFrostFSException( + String.format(INPUT_PARAM_GREATER_ZERO, Fields.count) + ); } this.count = count; @@ -19,18 +27,13 @@ public class Replica { public Replica(int count) { if (count <= 0) { - throw new IllegalArgumentException("Replica count must be positive"); + throw new ValidationFrostFSException( + String.format(INPUT_PARAM_GREATER_ZERO, Fields.count) + ); } + this.count = count; this.selector = EMPTY_STRING; } - - public int getCount() { - return count; - } - - public String getSelector() { - return selector; - } } diff --git a/models/src/main/java/info/frostfs/sdk/dto/netmap/Version.java b/models/src/main/java/info/frostfs/sdk/dto/netmap/Version.java index 7f9b54c..cab01cf 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/netmap/Version.java +++ b/models/src/main/java/info/frostfs/sdk/dto/netmap/Version.java @@ -1,31 +1,23 @@ package info.frostfs.sdk.dto.netmap; +import lombok.AllArgsConstructor; +import lombok.Getter; + import static info.frostfs.sdk.constants.AppConst.DEFAULT_MAJOR_VERSION; import static info.frostfs.sdk.constants.AppConst.DEFAULT_MINOR_VERSION; import static java.util.Objects.isNull; +@Getter +@AllArgsConstructor public class Version { private final int major; private final int minor; - public Version(int major, int minor) { - this.major = major; - this.minor = minor; - } - public Version() { this.major = DEFAULT_MAJOR_VERSION; this.minor = DEFAULT_MINOR_VERSION; } - public int getMajor() { - return major; - } - - public int getMinor() { - return minor; - } - @Override public String toString() { return "v" + major + "." + minor; diff --git a/models/src/main/java/info/frostfs/sdk/dto/object/ObjectAttribute.java b/models/src/main/java/info/frostfs/sdk/dto/object/ObjectAttribute.java index 60a3ede..b9602c2 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/object/ObjectAttribute.java +++ b/models/src/main/java/info/frostfs/sdk/dto/object/ObjectAttribute.java @@ -1,25 +1,22 @@ package info.frostfs.sdk.dto.object; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; +import lombok.Getter; import org.apache.commons.lang3.StringUtils; +import static info.frostfs.sdk.constants.ErrorConst.SOME_PARAM_IS_MISSING; + +@Getter public class ObjectAttribute { private final String key; private final String value; public ObjectAttribute(String key, String value) { if (StringUtils.isEmpty(key) || StringUtils.isEmpty(value)) { - throw new IllegalArgumentException("One of the input attributes is missing"); + throw new ValidationFrostFSException(SOME_PARAM_IS_MISSING); } this.key = key; this.value = value; } - - public String getKey() { - return key; - } - - public String getValue() { - return value; - } } diff --git a/models/src/main/java/info/frostfs/sdk/dto/object/ObjectFilter.java b/models/src/main/java/info/frostfs/sdk/dto/object/ObjectFilter.java index 575f5e1..ef046b5 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/object/ObjectFilter.java +++ b/models/src/main/java/info/frostfs/sdk/dto/object/ObjectFilter.java @@ -6,43 +6,18 @@ import info.frostfs.sdk.dto.CheckSum; import info.frostfs.sdk.dto.container.ContainerId; import info.frostfs.sdk.dto.netmap.Version; import info.frostfs.sdk.enums.ObjectMatchType; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; +@Getter +@Setter +@AllArgsConstructor public abstract class ObjectFilter { private ObjectMatchType matchType; private String key; private T value; - - public ObjectFilter(ObjectMatchType matchType, String key, T value) { - this.matchType = matchType; - this.key = key; - this.value = value; - } - - public ObjectMatchType getMatchType() { - return matchType; - } - - public void setMatchType(ObjectMatchType matchType) { - this.matchType = matchType; - } - - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - public T getValue() { - return value; - } - - public void setValue(T value) { - this.value = value; - } - public String getSerializedValue() { return value.toString(); } diff --git a/models/src/main/java/info/frostfs/sdk/dto/object/ObjectFrostFS.java b/models/src/main/java/info/frostfs/sdk/dto/object/ObjectFrostFS.java index 53195bd..155f973 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/object/ObjectFrostFS.java +++ b/models/src/main/java/info/frostfs/sdk/dto/object/ObjectFrostFS.java @@ -2,11 +2,17 @@ package info.frostfs.sdk.dto.object; import info.frostfs.sdk.dto.container.ContainerId; import info.frostfs.sdk.enums.ObjectType; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; +import lombok.Getter; +import lombok.Setter; import java.util.List; +import static info.frostfs.sdk.constants.ErrorConst.INPUT_PARAM_IS_MISSING_TEMPLATE; import static java.util.Objects.isNull; +@Getter +@Setter public class ObjectFrostFS { private final ObjectHeader header; private ObjectId objectId; @@ -15,7 +21,9 @@ public class ObjectFrostFS { public ObjectFrostFS(ObjectHeader header, ObjectId objectId, byte[] payload) { if (isNull(header)) { - throw new IllegalArgumentException("Object header is missing"); + throw new ValidationFrostFSException( + String.format(INPUT_PARAM_IS_MISSING_TEMPLATE, ObjectHeader.class.getName()) + ); } this.header = header; @@ -33,34 +41,6 @@ public class ObjectFrostFS { this.header = new ObjectHeader(containerId, objectType); } - public ObjectReader getObjectReader() { - return objectReader; - } - - public void setObjectReader(ObjectReader objectReader) { - this.objectReader = objectReader; - } - - public ObjectHeader getHeader() { - return header; - } - - public ObjectId getObjectId() { - return objectId; - } - - private void setObjectId(ObjectId objectId) { - this.objectId = objectId; - } - - public byte[] getPayload() { - return payload; - } - - public void setPayload(byte[] payload) { - this.payload = payload; - } - public void setSplit(Split split) { header.setSplit(split); } @@ -79,7 +59,9 @@ public class ObjectFrostFS { public void setParent(LargeObject largeObject) { if (isNull(header.getSplit())) { - throw new IllegalArgumentException("The object is not initialized properly"); + throw new ValidationFrostFSException( + String.format(INPUT_PARAM_IS_MISSING_TEMPLATE, Split.class.getName()) + ); } header.getSplit().setParentHeader(largeObject.getHeader()); diff --git a/models/src/main/java/info/frostfs/sdk/dto/object/ObjectHeader.java b/models/src/main/java/info/frostfs/sdk/dto/object/ObjectHeader.java index 3878736..0fdd3ef 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/object/ObjectHeader.java +++ b/models/src/main/java/info/frostfs/sdk/dto/object/ObjectHeader.java @@ -3,13 +3,20 @@ package info.frostfs.sdk.dto.object; import info.frostfs.sdk.dto.container.ContainerId; import info.frostfs.sdk.dto.netmap.Version; import info.frostfs.sdk.enums.ObjectType; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; +import lombok.Getter; +import lombok.Setter; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; +import static info.frostfs.sdk.constants.ErrorConst.FIELDS_DELIMITER_OR; +import static info.frostfs.sdk.constants.ErrorConst.INPUT_PARAM_IS_MISSING_TEMPLATE; import static java.util.Objects.isNull; +@Getter +@Setter public class ObjectHeader { private final ContainerId containerId; private final ObjectType objectType; @@ -23,7 +30,12 @@ public class ObjectHeader { public ObjectHeader(ContainerId containerId, ObjectType objectType, List attributes, long payloadLength, Version version) { if (isNull(containerId) || isNull(objectType)) { - throw new IllegalArgumentException("ContainerId or objectType is not present"); + throw new ValidationFrostFSException( + String.format( + INPUT_PARAM_IS_MISSING_TEMPLATE, + String.join(FIELDS_DELIMITER_OR, ContainerId.class.getName(), ObjectType.class.getName()) + ) + ); } this.attributes = attributes; @@ -47,67 +59,17 @@ public class ObjectHeader { this(containerId, ObjectType.REGULAR, attributes); } - public OwnerId getOwnerId() { - return ownerId; - } - - public void setOwnerId(OwnerId ownerId) { - this.ownerId = ownerId; - } - - public long getPayloadLength() { - return payloadLength; - } - - public void setPayloadLength(long payloadLength) { - this.payloadLength = payloadLength; - } - public void increasePayloadLength(long payloadLength) { this.payloadLength += payloadLength; } - public byte[] getPayloadCheckSum() { - return payloadCheckSum; - } - - public void setPayloadCheckSum(byte[] payloadCheckSum) { - this.payloadCheckSum = payloadCheckSum; - } - - public Split getSplit() { - return split; - } - public void setSplit(Split split) { if (isNull(split)) { - throw new IllegalArgumentException("Split is not present"); + throw new ValidationFrostFSException( + String.format(INPUT_PARAM_IS_MISSING_TEMPLATE, Split.class.getName()) + ); } this.split = split; } - - public List getAttributes() { - return attributes; - } - - public void setAttributes(List attributes) { - this.attributes = attributes; - } - - public ContainerId getContainerId() { - return containerId; - } - - public ObjectType getObjectType() { - return objectType; - } - - public Version getVersion() { - return version; - } - - public void setVersion(Version version) { - this.version = version; - } } diff --git a/models/src/main/java/info/frostfs/sdk/dto/object/ObjectId.java b/models/src/main/java/info/frostfs/sdk/dto/object/ObjectId.java index 7c6bffb..7f34f87 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/object/ObjectId.java +++ b/models/src/main/java/info/frostfs/sdk/dto/object/ObjectId.java @@ -2,16 +2,23 @@ package info.frostfs.sdk.dto.object; import info.frostfs.sdk.Base58; import info.frostfs.sdk.constants.AppConst; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; +import lombok.Getter; import org.apache.commons.lang3.StringUtils; +import static info.frostfs.sdk.constants.ErrorConst.INPUT_PARAM_IS_MISSING_TEMPLATE; +import static info.frostfs.sdk.constants.ErrorConst.INPUT_PARAM_IS_NOT_SHA256; import static java.util.Objects.isNull; +@Getter public class ObjectId { private final String value; public ObjectId(String value) { if (StringUtils.isEmpty(value)) { - throw new IllegalArgumentException("ObjectId value is missing"); + throw new ValidationFrostFSException( + String.format(INPUT_PARAM_IS_MISSING_TEMPLATE, ObjectId.class.getName()) + ); } this.value = value; @@ -19,16 +26,14 @@ public class ObjectId { public ObjectId(byte[] hash) { if (isNull(hash) || hash.length != AppConst.SHA256_HASH_LENGTH) { - throw new IllegalArgumentException("ObjectId must be a sha256 hash."); + throw new ValidationFrostFSException( + String.format(INPUT_PARAM_IS_NOT_SHA256, ObjectId.class.getName()) + ); } this.value = Base58.encode(hash); } - public String getValue() { - return value; - } - @Override public String toString() { return value; diff --git a/models/src/main/java/info/frostfs/sdk/dto/object/OwnerId.java b/models/src/main/java/info/frostfs/sdk/dto/object/OwnerId.java index e15a5eb..ba65652 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/object/OwnerId.java +++ b/models/src/main/java/info/frostfs/sdk/dto/object/OwnerId.java @@ -1,29 +1,37 @@ package info.frostfs.sdk.dto.object; import info.frostfs.sdk.Base58; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; import static info.frostfs.sdk.KeyExtension.publicKeyToAddress; +import static info.frostfs.sdk.constants.ErrorConst.INPUT_PARAM_IS_MISSING; +import static info.frostfs.sdk.constants.ErrorConst.INPUT_PARAM_IS_MISSING_TEMPLATE; import static java.util.Objects.isNull; +@Getter public class OwnerId { private final String value; public OwnerId(String value) { + if (StringUtils.isBlank(value)) { + throw new ValidationFrostFSException( + String.format(INPUT_PARAM_IS_MISSING_TEMPLATE, OwnerId.class.getName()) + ); + } + this.value = value; } public OwnerId(byte[] publicKey) { if (isNull(publicKey) || publicKey.length == 0) { - throw new IllegalArgumentException("PublicKey is invalid"); + throw new ValidationFrostFSException(INPUT_PARAM_IS_MISSING); } this.value = publicKeyToAddress(publicKey); } - public String getValue() { - return value; - } - public byte[] toHash() { return Base58.decode(value); } diff --git a/models/src/main/java/info/frostfs/sdk/dto/object/Split.java b/models/src/main/java/info/frostfs/sdk/dto/object/Split.java index 93836d2..7132de3 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/object/Split.java +++ b/models/src/main/java/info/frostfs/sdk/dto/object/Split.java @@ -2,12 +2,18 @@ package info.frostfs.sdk.dto.object; import frostfs.refs.Types; import info.frostfs.sdk.dto.response.Signature; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; +import lombok.Getter; +import lombok.Setter; import java.util.ArrayList; import java.util.List; +import static info.frostfs.sdk.constants.ErrorConst.INPUT_PARAM_IS_MISSING_TEMPLATE; import static java.util.Objects.isNull; +@Getter +@Setter public class Split { private final List children; private final SplitId splitId; @@ -23,58 +29,12 @@ public class Split { public Split(SplitId splitId) { if (isNull(splitId)) { - throw new IllegalArgumentException("SplitId is not present"); + throw new ValidationFrostFSException( + String.format(INPUT_PARAM_IS_MISSING_TEMPLATE, SplitId.class.getName()) + ); } this.splitId = splitId; this.children = new ArrayList<>(); } - - public SplitId getSplitId() { - return splitId; - } - - public ObjectId getParent() { - return parent; - } - - public void setParent(ObjectId parent) { - this.parent = parent; - } - - public ObjectId getPrevious() { - return previous; - } - - public void setPrevious(ObjectId previous) { - this.previous = previous; - } - - public Signature getParentSignature() { - return parentSignature; - } - - public void setParentSignature(Signature parentSignature) { - this.parentSignature = parentSignature; - } - - public ObjectHeader getParentHeader() { - return parentHeader; - } - - public void setParentHeader(ObjectHeader parentHeader) { - this.parentHeader = parentHeader; - } - - public List getChildren() { - return children; - } - - public Types.Signature getParentSignatureGrpc() { - return parentSignatureGrpc; - } - - public void setParentSignatureGrpc(Types.Signature parentSignatureGrpc) { - this.parentSignatureGrpc = parentSignatureGrpc; - } } diff --git a/models/src/main/java/info/frostfs/sdk/dto/object/SplitId.java b/models/src/main/java/info/frostfs/sdk/dto/object/SplitId.java index 994d184..260d739 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/object/SplitId.java +++ b/models/src/main/java/info/frostfs/sdk/dto/object/SplitId.java @@ -1,11 +1,14 @@ package info.frostfs.sdk.dto.object; +import lombok.Getter; + import java.util.UUID; import static info.frostfs.sdk.UuidExtension.asBytes; import static info.frostfs.sdk.UuidExtension.asUuid; import static java.util.Objects.isNull; +@Getter public class SplitId { private final UUID id; diff --git a/models/src/main/java/info/frostfs/sdk/dto/response/MetaHeader.java b/models/src/main/java/info/frostfs/sdk/dto/response/MetaHeader.java index 9a1faff..af1d2ab 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/response/MetaHeader.java +++ b/models/src/main/java/info/frostfs/sdk/dto/response/MetaHeader.java @@ -1,24 +1,26 @@ package info.frostfs.sdk.dto.response; import info.frostfs.sdk.dto.netmap.Version; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; +import lombok.Getter; +import lombok.experimental.FieldNameConstants; import static info.frostfs.sdk.constants.AppConst.DEFAULT_MAJOR_VERSION; import static info.frostfs.sdk.constants.AppConst.DEFAULT_MINOR_VERSION; +import static info.frostfs.sdk.constants.ErrorConst.*; import static java.util.Objects.isNull; +@Getter +@FieldNameConstants public class MetaHeader { private Version version; private int epoch; private int ttl; public MetaHeader(Version version, int epoch, int ttl) { - if (isNull(version) || epoch < 0 || ttl <= 0) { - throw new IllegalArgumentException("One of the input attributes is invalid or missing"); - } - - this.version = version; - this.epoch = epoch; - this.ttl = ttl; + setVersion(version); + setEpoch(epoch); + setTtl(ttl); } public MetaHeader() { @@ -27,37 +29,31 @@ public class MetaHeader { this.ttl = 2; } - public Version getVersion() { - return version; - } - public void setVersion(Version version) { if (isNull(version)) { - throw new IllegalArgumentException("Version is missing."); + throw new ValidationFrostFSException( + String.format(INPUT_PARAM_IS_MISSING_TEMPLATE, Version.class.getName()) + ); } this.version = version; } - public int getEpoch() { - return epoch; - } - public void setEpoch(int epoch) { if (epoch < 0) { - throw new IllegalArgumentException("The epoch must be greater than or equal to zero."); + throw new ValidationFrostFSException( + String.format(INPUT_PARAM_GREATER_OR_EQUAL_ZERO, Fields.epoch) + ); } this.epoch = epoch; } - public int getTtl() { - return ttl; - } - public void setTtl(int ttl) { if (ttl <= 0) { - throw new IllegalArgumentException("The ttl must be greater than zero."); + throw new ValidationFrostFSException( + String.format(INPUT_PARAM_GREATER_ZERO, Fields.ttl) + ); } this.ttl = ttl; diff --git a/models/src/main/java/info/frostfs/sdk/dto/response/ResponseStatus.java b/models/src/main/java/info/frostfs/sdk/dto/response/ResponseStatus.java index 66e7031..af69f98 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/response/ResponseStatus.java +++ b/models/src/main/java/info/frostfs/sdk/dto/response/ResponseStatus.java @@ -1,10 +1,14 @@ package info.frostfs.sdk.dto.response; import info.frostfs.sdk.enums.StatusCode; +import lombok.Getter; +import lombok.Setter; import static info.frostfs.sdk.constants.FieldConst.EMPTY_STRING; import static java.util.Objects.isNull; +@Getter +@Setter public class ResponseStatus { private StatusCode code; private String message; @@ -19,22 +23,6 @@ public class ResponseStatus { this.message = EMPTY_STRING; } - public StatusCode getCode() { - return code; - } - - public void setCode(StatusCode code) { - this.code = code; - } - - public String getMessage() { - return message; - } - - public void setMessage(String message) { - this.message = message; - } - @Override public String toString() { return String.format("Response status: %s. Message: %s.", code, message); diff --git a/models/src/main/java/info/frostfs/sdk/dto/response/Signature.java b/models/src/main/java/info/frostfs/sdk/dto/response/Signature.java index 65d6ce8..2a871a3 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/response/Signature.java +++ b/models/src/main/java/info/frostfs/sdk/dto/response/Signature.java @@ -1,33 +1,13 @@ package info.frostfs.sdk.dto.response; import info.frostfs.sdk.enums.SignatureScheme; +import lombok.Getter; +import lombok.Setter; +@Getter +@Setter public class Signature { private byte[] key; private byte[] sign; private SignatureScheme scheme; - - public byte[] getKey() { - return key; - } - - public void setKey(byte[] key) { - this.key = key; - } - - public byte[] getSign() { - return sign; - } - - public void setSign(byte[] sign) { - this.sign = sign; - } - - public SignatureScheme getScheme() { - return scheme; - } - - public void setScheme(SignatureScheme scheme) { - this.scheme = scheme; - } } diff --git a/models/src/main/java/info/frostfs/sdk/dto/session/SessionToken.java b/models/src/main/java/info/frostfs/sdk/dto/session/SessionToken.java index 2c4ae83..eb4e0a3 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/session/SessionToken.java +++ b/models/src/main/java/info/frostfs/sdk/dto/session/SessionToken.java @@ -1,13 +1,10 @@ package info.frostfs.sdk.dto.session; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor public class SessionToken { private final byte[] token; - - public SessionToken(byte[] token) { - this.token = token; - } - - public byte[] getToken() { - return token; - } } diff --git a/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerMapper.java index e874859..76f8479 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerMapper.java @@ -4,16 +4,16 @@ import com.google.protobuf.ByteString; import frostfs.container.Types; import info.frostfs.sdk.dto.container.Container; import info.frostfs.sdk.enums.BasicAcl; +import info.frostfs.sdk.exceptions.ProcessFrostFSException; import info.frostfs.sdk.mappers.netmap.PlacementPolicyMapper; import info.frostfs.sdk.mappers.netmap.VersionMapper; import static info.frostfs.sdk.UuidExtension.asBytes; import static info.frostfs.sdk.UuidExtension.asUuid; +import static info.frostfs.sdk.constants.ErrorConst.UNKNOWN_ENUM_VALUE_TEMPLATE; import static java.util.Objects.isNull; public class ContainerMapper { - private static final String ERROR_UNKNOWN_VALUE_TEMPLATE = "Unknown BasicACL rule. Value: %s."; - private ContainerMapper() { } @@ -36,8 +36,8 @@ public class ContainerMapper { var basicAcl = BasicAcl.get(containerGrpc.getBasicAcl()); if (isNull(basicAcl)) { - throw new IllegalArgumentException( - String.format(ERROR_UNKNOWN_VALUE_TEMPLATE, containerGrpc.getBasicAcl()) + throw new ProcessFrostFSException( + String.format(UNKNOWN_ENUM_VALUE_TEMPLATE, BasicAcl.class.getName(), containerGrpc.getBasicAcl()) ); } diff --git a/models/src/main/java/info/frostfs/sdk/mappers/netmap/NodeInfoMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/netmap/NodeInfoMapper.java index 97fa499..f319583 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/netmap/NodeInfoMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/netmap/NodeInfoMapper.java @@ -5,14 +5,14 @@ import frostfs.netmap.Types; import frostfs.netmap.Types.NodeInfo.Attribute; import info.frostfs.sdk.dto.netmap.NodeInfo; import info.frostfs.sdk.enums.NodeState; +import info.frostfs.sdk.exceptions.ProcessFrostFSException; import java.util.stream.Collectors; +import static info.frostfs.sdk.constants.ErrorConst.UNKNOWN_ENUM_VALUE_TEMPLATE; import static java.util.Objects.isNull; public class NodeInfoMapper { - private static final String ERROR_UNKNOWN_VALUE_TEMPLATE = "Unknown NodeState. Value: %s."; - private NodeInfoMapper() { } @@ -31,7 +31,9 @@ public class NodeInfoMapper { NodeState nodeState = NodeState.get(nodeInfo.getState().getNumber()); if (isNull(nodeState)) { - throw new IllegalArgumentException(String.format(ERROR_UNKNOWN_VALUE_TEMPLATE, nodeInfo.getState())); + throw new ProcessFrostFSException( + String.format(UNKNOWN_ENUM_VALUE_TEMPLATE, NodeState.class.getName(), nodeInfo.getState()) + ); } return new NodeInfo( diff --git a/models/src/main/java/info/frostfs/sdk/mappers/netmap/PlacementPolicyMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/netmap/PlacementPolicyMapper.java index 191a080..22c65bb 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/netmap/PlacementPolicyMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/netmap/PlacementPolicyMapper.java @@ -7,7 +7,6 @@ import info.frostfs.sdk.dto.netmap.Replica; import static java.util.Objects.isNull; public class PlacementPolicyMapper { - private PlacementPolicyMapper() { } @@ -32,8 +31,8 @@ public class PlacementPolicyMapper { } return new PlacementPolicy( - placementPolicy.getUnique(), - placementPolicy.getReplicasList().stream().map(ReplicaMapper::toModel).toArray(Replica[]::new) + placementPolicy.getReplicasList().stream().map(ReplicaMapper::toModel).toArray(Replica[]::new), + placementPolicy.getUnique() ); } } diff --git a/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectFilterMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectFilterMapper.java index 8ebf944..a9f2c7d 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectFilterMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectFilterMapper.java @@ -3,12 +3,12 @@ package info.frostfs.sdk.mappers.object; import frostfs.object.Service; import frostfs.object.Types; import info.frostfs.sdk.dto.object.ObjectFilter; +import info.frostfs.sdk.exceptions.ProcessFrostFSException; +import static info.frostfs.sdk.constants.ErrorConst.UNKNOWN_ENUM_VALUE_TEMPLATE; import static java.util.Objects.isNull; public class ObjectFilterMapper { - private static final String ERROR_UNKNOWN_VALUE_TEMPLATE = "Unknown MatchType. Value: %s."; - private ObjectFilterMapper() { } @@ -19,9 +19,11 @@ public class ObjectFilterMapper { var objectMatchType = Types.MatchType.forNumber(filter.getMatchType().value); if (isNull(objectMatchType)) { - throw new IllegalArgumentException( - String.format(ERROR_UNKNOWN_VALUE_TEMPLATE, filter.getMatchType().name()) - ); + throw new ProcessFrostFSException(String.format( + UNKNOWN_ENUM_VALUE_TEMPLATE, + Types.MatchType.class.getName(), + filter.getMatchType().name() + )); } return Service.SearchRequest.Body.Filter.newBuilder() diff --git a/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectHeaderMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectHeaderMapper.java index 93c432c..2deeef6 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectHeaderMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectHeaderMapper.java @@ -6,32 +6,37 @@ import info.frostfs.sdk.dto.container.ContainerId; import info.frostfs.sdk.dto.object.ObjectAttribute; import info.frostfs.sdk.dto.object.ObjectHeader; import info.frostfs.sdk.enums.ObjectType; +import info.frostfs.sdk.exceptions.ProcessFrostFSException; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; import info.frostfs.sdk.mappers.container.ContainerIdMapper; import info.frostfs.sdk.mappers.netmap.VersionMapper; import org.apache.commons.collections4.ListUtils; import java.util.stream.Collectors; +import static info.frostfs.sdk.constants.ErrorConst.INPUT_PARAM_IS_MISSING_TEMPLATE; +import static info.frostfs.sdk.constants.ErrorConst.UNKNOWN_ENUM_VALUE_TEMPLATE; import static java.util.Objects.isNull; import static java.util.Objects.nonNull; public class ObjectHeaderMapper { - private static final String ERROR_UNKNOWN_VALUE_TEMPLATE = "Unknown ObjectType. Value: %s."; - private static final String ERROR_OBJECT_HEADER_MISSING_ERROR = "ObjectHeader is not present"; - private ObjectHeaderMapper() { } public static Types.Header toGrpcMessage(ObjectHeader header) { if (isNull(header)) { - throw new IllegalArgumentException(ERROR_OBJECT_HEADER_MISSING_ERROR); + throw new ValidationFrostFSException( + String.format(INPUT_PARAM_IS_MISSING_TEMPLATE, ObjectHeader.class.getName()) + ); } var objectType = Types.ObjectType.forNumber(header.getObjectType().value); if (isNull(objectType)) { - throw new IllegalArgumentException( - String.format(ERROR_UNKNOWN_VALUE_TEMPLATE, header.getObjectType().name()) - ); + throw new ProcessFrostFSException(String.format( + UNKNOWN_ENUM_VALUE_TEMPLATE, + Types.ObjectType.class.getName(), + header.getObjectType().name() + )); } var head = Types.Header.newBuilder() @@ -62,8 +67,8 @@ public class ObjectHeaderMapper { var objectType = ObjectType.get(header.getObjectTypeValue()); if (isNull(objectType)) { - throw new IllegalArgumentException( - String.format(ERROR_UNKNOWN_VALUE_TEMPLATE, header.getObjectType()) + throw new ProcessFrostFSException( + String.format(UNKNOWN_ENUM_VALUE_TEMPLATE, ObjectType.class.getName(), header.getObjectType()) ); } diff --git a/models/src/main/java/info/frostfs/sdk/mappers/response/MetaHeaderMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/response/MetaHeaderMapper.java index d8e7324..c9031b4 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/response/MetaHeaderMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/response/MetaHeaderMapper.java @@ -2,19 +2,21 @@ package info.frostfs.sdk.mappers.response; import frostfs.session.Types; import info.frostfs.sdk.dto.response.MetaHeader; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; import info.frostfs.sdk.mappers.netmap.VersionMapper; +import static info.frostfs.sdk.constants.ErrorConst.INPUT_PARAM_IS_MISSING_TEMPLATE; import static java.util.Objects.isNull; public class MetaHeaderMapper { - private static final String ERROR_META_HEADER_MISSING_ERROR = "MetaHeader is not present"; - private MetaHeaderMapper() { } public static Types.RequestMetaHeader toGrpcMessage(MetaHeader metaHeader) { if (isNull(metaHeader)) { - throw new IllegalArgumentException(ERROR_META_HEADER_MISSING_ERROR); + throw new ValidationFrostFSException( + String.format(INPUT_PARAM_IS_MISSING_TEMPLATE, MetaHeader.class.getName()) + ); } return toGrpcMessageBuilder(metaHeader).build(); @@ -22,7 +24,9 @@ public class MetaHeaderMapper { public static Types.RequestMetaHeader.Builder toGrpcMessageBuilder(MetaHeader metaHeader) { if (isNull(metaHeader)) { - throw new IllegalArgumentException(ERROR_META_HEADER_MISSING_ERROR); + throw new ValidationFrostFSException( + String.format(INPUT_PARAM_IS_MISSING_TEMPLATE, MetaHeader.class.getName()) + ); } return Types.RequestMetaHeader.newBuilder() diff --git a/models/src/main/java/info/frostfs/sdk/mappers/response/ResponseStatusMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/response/ResponseStatusMapper.java index 192ea21..cb935dd 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/response/ResponseStatusMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/response/ResponseStatusMapper.java @@ -3,12 +3,12 @@ package info.frostfs.sdk.mappers.response; import frostfs.status.Types; import info.frostfs.sdk.dto.response.ResponseStatus; import info.frostfs.sdk.enums.StatusCode; +import info.frostfs.sdk.exceptions.ProcessFrostFSException; +import static info.frostfs.sdk.constants.ErrorConst.UNKNOWN_ENUM_VALUE_TEMPLATE; import static java.util.Objects.isNull; public class ResponseStatusMapper { - private static final String ERROR_UNKNOWN_VALUE_TEMPLATE = "Unknown StatusCode. Value: %s."; - private ResponseStatusMapper() { } @@ -19,8 +19,8 @@ public class ResponseStatusMapper { var statusCode = StatusCode.get(status.getCode()); if (isNull(statusCode)) { - throw new IllegalArgumentException( - String.format(ERROR_UNKNOWN_VALUE_TEMPLATE, status.getCode()) + throw new ProcessFrostFSException( + String.format(UNKNOWN_ENUM_VALUE_TEMPLATE, StatusCode.class.getName(), status.getCode()) ); } diff --git a/models/src/main/java/info/frostfs/sdk/mappers/response/SignatureMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/response/SignatureMapper.java index 162d5a5..02905ac 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/response/SignatureMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/response/SignatureMapper.java @@ -3,12 +3,12 @@ package info.frostfs.sdk.mappers.response; import com.google.protobuf.ByteString; import frostfs.refs.Types; import info.frostfs.sdk.dto.response.Signature; +import info.frostfs.sdk.exceptions.ProcessFrostFSException; +import static info.frostfs.sdk.constants.ErrorConst.UNKNOWN_ENUM_VALUE_TEMPLATE; import static java.util.Objects.isNull; public class SignatureMapper { - private static final String ERROR_UNKNOWN_VALUE_TEMPLATE = "Unknown SignatureScheme. Value: %s."; - private SignatureMapper() { } @@ -19,9 +19,11 @@ public class SignatureMapper { var scheme = Types.SignatureScheme.forNumber(signature.getScheme().value); if (isNull(scheme)) { - throw new IllegalArgumentException( - String.format(ERROR_UNKNOWN_VALUE_TEMPLATE, signature.getScheme().name()) - ); + throw new ProcessFrostFSException(String.format( + UNKNOWN_ENUM_VALUE_TEMPLATE, + Types.SignatureScheme.class.getName(), + signature.getScheme().name() + )); } return Types.Signature.newBuilder() diff --git a/models/src/main/java/info/frostfs/sdk/mappers/session/SessionMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/session/SessionMapper.java index 1536f35..e9f76e5 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/session/SessionMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/session/SessionMapper.java @@ -3,20 +3,23 @@ package info.frostfs.sdk.mappers.session; import com.google.protobuf.CodedOutputStream; import com.google.protobuf.InvalidProtocolBufferException; import frostfs.session.Types; +import info.frostfs.sdk.exceptions.ProcessFrostFSException; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; import java.io.IOException; +import static info.frostfs.sdk.constants.ErrorConst.INPUT_PARAM_IS_MISSING_TEMPLATE; import static java.util.Objects.isNull; public class SessionMapper { - private static final String ERROR_TOKEN_MISSING_MESSAGE = "Token is not present"; - private SessionMapper() { } public static byte[] serialize(Types.SessionToken token) { if (isNull(token)) { - throw new IllegalArgumentException(ERROR_TOKEN_MISSING_MESSAGE); + throw new ValidationFrostFSException( + String.format(INPUT_PARAM_IS_MISSING_TEMPLATE, Types.SessionToken.class.getName()) + ); } try { @@ -25,19 +28,21 @@ public class SessionMapper { token.writeTo(stream); return bytes; } catch (IOException exp) { - throw new IllegalArgumentException(exp.getMessage()); + throw new ProcessFrostFSException(exp.getMessage()); } } public static Types.SessionToken deserializeSessionToken(byte[] bytes) { if (isNull(bytes) || bytes.length == 0) { - throw new IllegalArgumentException(ERROR_TOKEN_MISSING_MESSAGE); + throw new ValidationFrostFSException( + String.format(INPUT_PARAM_IS_MISSING_TEMPLATE, Types.SessionToken.class.getName()) + ); } try { return Types.SessionToken.newBuilder().mergeFrom(bytes).build(); } catch (InvalidProtocolBufferException exp) { - throw new IllegalArgumentException(exp.getMessage()); + throw new ProcessFrostFSException(exp.getMessage()); } } } diff --git a/models/src/test/java/info/frostfs/sdk/UuidExtensionTest.java b/models/src/test/java/info/frostfs/sdk/UuidExtensionTest.java index 6cc40e0..9353b73 100644 --- a/models/src/test/java/info/frostfs/sdk/UuidExtensionTest.java +++ b/models/src/test/java/info/frostfs/sdk/UuidExtensionTest.java @@ -1,5 +1,6 @@ package info.frostfs.sdk; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; import org.junit.jupiter.api.Test; import java.util.UUID; @@ -25,7 +26,7 @@ public class UuidExtensionTest { @Test void uuidAsBytes_givenParamsIsNull() { //When + Then - assertThrows(IllegalArgumentException.class, () -> UuidExtension.asBytes(null)); + assertThrows(ValidationFrostFSException.class, () -> UuidExtension.asBytes(null)); } @Test @@ -40,7 +41,7 @@ public class UuidExtensionTest { @Test void bytesAsUuid_givenParamsIsNull() { //When + Then - assertThrows(IllegalArgumentException.class, () -> UuidExtension.asUuid(null)); + assertThrows(ValidationFrostFSException.class, () -> UuidExtension.asUuid(null)); } @Test @@ -51,7 +52,7 @@ public class UuidExtensionTest { //When + Then - assertThrows(IllegalArgumentException.class, () -> UuidExtension.asUuid(valueLength15)); - assertThrows(IllegalArgumentException.class, () -> UuidExtension.asUuid(valueLength17)); + assertThrows(ValidationFrostFSException.class, () -> UuidExtension.asUuid(valueLength15)); + assertThrows(ValidationFrostFSException.class, () -> UuidExtension.asUuid(valueLength17)); } } diff --git a/models/src/test/java/info/frostfs/sdk/mappers/container/ContainerMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/container/ContainerMapperTest.java index 7db4345..d4c6dd4 100644 --- a/models/src/test/java/info/frostfs/sdk/mappers/container/ContainerMapperTest.java +++ b/models/src/test/java/info/frostfs/sdk/mappers/container/ContainerMapperTest.java @@ -6,6 +6,7 @@ import info.frostfs.sdk.dto.container.Container; import info.frostfs.sdk.dto.netmap.PlacementPolicy; import info.frostfs.sdk.dto.netmap.Replica; import info.frostfs.sdk.enums.BasicAcl; +import info.frostfs.sdk.exceptions.ProcessFrostFSException; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; @@ -21,7 +22,7 @@ public class ContainerMapperTest { @Test void toGrpcMessage_success() { //Given - var placementPolicy = new PlacementPolicy(true, new Replica[]{new Replica(1)}); + var placementPolicy = new PlacementPolicy(new Replica[]{new Replica(1)}, true); var container = new Container(BasicAcl.PUBLIC_RW, placementPolicy); //When @@ -111,6 +112,6 @@ public class ContainerMapperTest { .build(); //When + Then - assertThrows(IllegalArgumentException.class, () -> ContainerMapper.toModel(container)); + assertThrows(ProcessFrostFSException.class, () -> ContainerMapper.toModel(container)); } } diff --git a/models/src/test/java/info/frostfs/sdk/mappers/netmap/NodeInfoMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/netmap/NodeInfoMapperTest.java index c60fceb..e2b6c29 100644 --- a/models/src/test/java/info/frostfs/sdk/mappers/netmap/NodeInfoMapperTest.java +++ b/models/src/test/java/info/frostfs/sdk/mappers/netmap/NodeInfoMapperTest.java @@ -4,6 +4,7 @@ import com.google.protobuf.ByteString; import frostfs.netmap.Service; import frostfs.netmap.Types; import info.frostfs.sdk.enums.NodeState; +import info.frostfs.sdk.exceptions.ProcessFrostFSException; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; @@ -117,7 +118,7 @@ public class NodeInfoMapperTest { mockStatic.when(() -> NodeState.get(Types.NodeInfo.State.ONLINE.getNumber())) .thenReturn(null); - assertThrows(IllegalArgumentException.class, () -> NodeInfoMapper.toModel(body)); + assertThrows(ProcessFrostFSException.class, () -> NodeInfoMapper.toModel(body)); } } } diff --git a/models/src/test/java/info/frostfs/sdk/mappers/netmap/PlacementPolicyMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/netmap/PlacementPolicyMapperTest.java index 3cc57ae..903d574 100644 --- a/models/src/test/java/info/frostfs/sdk/mappers/netmap/PlacementPolicyMapperTest.java +++ b/models/src/test/java/info/frostfs/sdk/mappers/netmap/PlacementPolicyMapperTest.java @@ -15,7 +15,7 @@ public class PlacementPolicyMapperTest { var replica1 = new Replica(1, "test1"); var replica2 = new Replica(2, "test2"); - var placementPolicy = new PlacementPolicy(true, new Replica[]{replica1, replica2}); + var placementPolicy = new PlacementPolicy(new Replica[]{replica1, replica2}, true); //When var result = PlacementPolicyMapper.toGrpcMessage(placementPolicy); diff --git a/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectFilterMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectFilterMapperTest.java index 7bc8aec..9fbc109 100644 --- a/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectFilterMapperTest.java +++ b/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectFilterMapperTest.java @@ -3,6 +3,7 @@ package info.frostfs.sdk.mappers.object; import frostfs.object.Types; import info.frostfs.sdk.dto.object.ObjectFilter; import info.frostfs.sdk.enums.ObjectMatchType; +import info.frostfs.sdk.exceptions.ProcessFrostFSException; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; @@ -47,7 +48,7 @@ public class ObjectFilterMapperTest { mockStatic.when(() -> Types.MatchType.forNumber(objectFilter.getMatchType().value)) .thenReturn(null); - assertThrows(IllegalArgumentException.class, () -> ObjectFilterMapper.toGrpcMessage(objectFilter)); + assertThrows(ProcessFrostFSException.class, () -> ObjectFilterMapper.toGrpcMessage(objectFilter)); } } } diff --git a/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectHeaderMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectHeaderMapperTest.java index 207854d..76a7fe9 100644 --- a/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectHeaderMapperTest.java +++ b/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectHeaderMapperTest.java @@ -8,7 +8,8 @@ import info.frostfs.sdk.dto.object.ObjectAttribute; import info.frostfs.sdk.dto.object.ObjectHeader; import info.frostfs.sdk.dto.object.OwnerId; import info.frostfs.sdk.enums.ObjectType; -import info.frostfs.sdk.mappers.response.MetaHeaderMapper; +import info.frostfs.sdk.exceptions.ProcessFrostFSException; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; @@ -56,7 +57,7 @@ public class ObjectHeaderMapperTest { @Test void toGrpcMessage_null() { //When + Then - assertThrows(IllegalArgumentException.class, () -> ObjectHeaderMapper.toGrpcMessage(null)); + assertThrows(ValidationFrostFSException.class, () -> ObjectHeaderMapper.toGrpcMessage(null)); } @Test @@ -70,7 +71,7 @@ public class ObjectHeaderMapperTest { mockStatic.when(() -> Types.ObjectType.forNumber(objectHeader.getObjectType().value)) .thenReturn(null); - assertThrows(IllegalArgumentException.class, () -> ObjectHeaderMapper.toGrpcMessage(objectHeader)); + assertThrows(ProcessFrostFSException.class, () -> ObjectHeaderMapper.toGrpcMessage(objectHeader)); } } @@ -125,7 +126,7 @@ public class ObjectHeaderMapperTest { mockStatic.when(() -> ObjectType.get(Types.ObjectType.TOMBSTONE.getNumber())) .thenReturn(null); - assertThrows(IllegalArgumentException.class, () -> ObjectHeaderMapper.toModel(header)); + assertThrows(ProcessFrostFSException.class, () -> ObjectHeaderMapper.toModel(header)); } } } diff --git a/models/src/test/java/info/frostfs/sdk/mappers/response/MetaHeaderMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/response/MetaHeaderMapperTest.java index 3c48b52..ad13e4f 100644 --- a/models/src/test/java/info/frostfs/sdk/mappers/response/MetaHeaderMapperTest.java +++ b/models/src/test/java/info/frostfs/sdk/mappers/response/MetaHeaderMapperTest.java @@ -3,6 +3,7 @@ package info.frostfs.sdk.mappers.response; import frostfs.session.Types; import info.frostfs.sdk.dto.netmap.Version; import info.frostfs.sdk.dto.response.MetaHeader; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; @@ -29,7 +30,7 @@ public class MetaHeaderMapperTest { @Test void toGrpcMessage_null() { //When + Then - assertThrows(IllegalArgumentException.class, () -> MetaHeaderMapper.toGrpcMessage(null)); + assertThrows(ValidationFrostFSException.class, () -> MetaHeaderMapper.toGrpcMessage(null)); } @Test @@ -52,6 +53,6 @@ public class MetaHeaderMapperTest { @Test void toGrpcMessageBuilder_null() { //When + Then - assertThrows(IllegalArgumentException.class, () -> MetaHeaderMapper.toGrpcMessageBuilder(null)); + assertThrows(ValidationFrostFSException.class, () -> MetaHeaderMapper.toGrpcMessageBuilder(null)); } } diff --git a/models/src/test/java/info/frostfs/sdk/mappers/response/ResponseStatusMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/response/ResponseStatusMapperTest.java index d3073e2..09bb763 100644 --- a/models/src/test/java/info/frostfs/sdk/mappers/response/ResponseStatusMapperTest.java +++ b/models/src/test/java/info/frostfs/sdk/mappers/response/ResponseStatusMapperTest.java @@ -2,6 +2,7 @@ package info.frostfs.sdk.mappers.response; import frostfs.status.Types; import info.frostfs.sdk.enums.StatusCode; +import info.frostfs.sdk.exceptions.ProcessFrostFSException; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; @@ -48,6 +49,6 @@ public class ResponseStatusMapperTest { .build(); //When + Then - assertThrows(IllegalArgumentException.class, () -> ResponseStatusMapper.toModel(status)); + assertThrows(ProcessFrostFSException.class, () -> ResponseStatusMapper.toModel(status)); } } diff --git a/models/src/test/java/info/frostfs/sdk/mappers/response/SignatureMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/response/SignatureMapperTest.java index 471c0aa..c9a4474 100644 --- a/models/src/test/java/info/frostfs/sdk/mappers/response/SignatureMapperTest.java +++ b/models/src/test/java/info/frostfs/sdk/mappers/response/SignatureMapperTest.java @@ -3,6 +3,7 @@ package info.frostfs.sdk.mappers.response; import frostfs.refs.Types; import info.frostfs.sdk.dto.response.Signature; import info.frostfs.sdk.enums.SignatureScheme; +import info.frostfs.sdk.exceptions.ProcessFrostFSException; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; @@ -50,7 +51,7 @@ public class SignatureMapperTest { mockStatic.when(() -> Types.SignatureScheme.forNumber(signature.getScheme().value)) .thenReturn(null); - assertThrows(IllegalArgumentException.class, () -> SignatureMapper.toGrpcMessage(signature)); + assertThrows(ProcessFrostFSException.class, () -> SignatureMapper.toGrpcMessage(signature)); } } } diff --git a/models/src/test/java/info/frostfs/sdk/mappers/session/SessionMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/session/SessionMapperTest.java index 3f7d0ea..b85e0ce 100644 --- a/models/src/test/java/info/frostfs/sdk/mappers/session/SessionMapperTest.java +++ b/models/src/test/java/info/frostfs/sdk/mappers/session/SessionMapperTest.java @@ -2,6 +2,7 @@ package info.frostfs.sdk.mappers.session; import com.google.protobuf.ByteString; import frostfs.session.Types; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -27,7 +28,7 @@ public class SessionMapperTest { @Test void serialize_wrong() { //When + Then - assertThrows(IllegalArgumentException.class, () -> SessionMapper.serialize(null)); + assertThrows(ValidationFrostFSException.class, () -> SessionMapper.serialize(null)); } @Test @@ -43,8 +44,8 @@ public class SessionMapperTest { @Test void deserialize_wrong() { //When + Then - assertThrows(IllegalArgumentException.class, () -> SessionMapper.deserializeSessionToken(null)); - assertThrows(IllegalArgumentException.class, () -> SessionMapper.deserializeSessionToken(new byte[]{})); + assertThrows(ValidationFrostFSException.class, () -> SessionMapper.deserializeSessionToken(null)); + assertThrows(ValidationFrostFSException.class, () -> SessionMapper.deserializeSessionToken(new byte[]{})); } private Types.SessionToken createSessionToken() { diff --git a/pom.xml b/pom.xml index 9b5a976..ed517d6 100644 --- a/pom.xml +++ b/pom.xml @@ -13,6 +13,7 @@ cryptography models protos + exceptions @@ -23,9 +24,28 @@ 5.12.0 5.10.3 3.26.3 + 1.18.34 + 3.23.0 + + org.apache.commons + commons-collections4 + 4.4 + + + org.apache.commons + commons-lang3 + 3.14.0 + + + org.projectlombok + lombok + ${lombok.version} + provided + true + org.junit.jupiter junit-jupiter diff --git a/protos/pom.xml b/protos/pom.xml index d2e06dc..5381838 100644 --- a/protos/pom.xml +++ b/protos/pom.xml @@ -14,7 +14,6 @@ 11 11 - 3.23.0 1.65.1 UTF-8 From 59356180d5008842ad5f62d37c4ae577ee2fb176 Mon Sep 17 00:00:00 2001 From: Bruk Ori Date: Wed, 18 Sep 2024 10:33:37 +0300 Subject: [PATCH 18/38] [#14] code review fix Signed-off-by: Ori Bruk --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a7cf481..737c204 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ public class ContainerExample { FrostFSClient frostFSClient = new FrostFSClient(clientSettings); // Create container - var placementPolicy = new PlacementPolicy(new Replica[]{new Replica(1)}, true); + var placementPolicy = new PlacementPolicy(new Replica[]{new Replica(1)}, Boolean.TRUE); var containerId = frostFSClient.createContainer(new Container(BasicAcl.PUBLIC_RW, placementPolicy)); // Get container From f01c92784d63452a543e9bde19fa4508273aeea0 Mon Sep 17 00:00:00 2001 From: Pavel Pogodaev Date: Fri, 27 Sep 2024 14:54:09 +0300 Subject: [PATCH 19/38] [#16] Add GRPC metrics interceptor Signed-off-by: Pavel Pogodaev --- client/pom.xml | 17 +- .../java/info/frostfs/sdk/FrostFSClient.java | 6 + .../impl/interceptor/ClientMetrics.java | 170 ++++++++++++++++++ .../impl/interceptor/Configuration.java | 153 ++++++++++++++++ .../services/impl/interceptor/GrpcMethod.java | 45 +++++ .../sdk/services/impl/interceptor/Labels.java | 53 ++++++ .../interceptor/MonitoringClientCall.java | 47 +++++ .../MonitoringClientCallListener.java | 61 +++++++ .../MonitoringClientInterceptor.java | 35 ++++ .../main/java/info/frostfs/sdk/Base58.java | 3 +- 10 files changed, 587 insertions(+), 3 deletions(-) create mode 100644 client/src/main/java/info/frostfs/sdk/services/impl/interceptor/ClientMetrics.java create mode 100644 client/src/main/java/info/frostfs/sdk/services/impl/interceptor/Configuration.java create mode 100644 client/src/main/java/info/frostfs/sdk/services/impl/interceptor/GrpcMethod.java create mode 100644 client/src/main/java/info/frostfs/sdk/services/impl/interceptor/Labels.java create mode 100644 client/src/main/java/info/frostfs/sdk/services/impl/interceptor/MonitoringClientCall.java create mode 100644 client/src/main/java/info/frostfs/sdk/services/impl/interceptor/MonitoringClientCallListener.java create mode 100644 client/src/main/java/info/frostfs/sdk/services/impl/interceptor/MonitoringClientInterceptor.java diff --git a/client/pom.xml b/client/pom.xml index c920473..a3f7ff7 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -38,6 +38,21 @@ commons-codec 1.17.0 + + + io.prometheus + simpleclient + 0.16.0 + + + io.prometheus + simpleclient_hotspot + 0.16.0 + + + io.prometheus + simpleclient_common + 0.16.0 + - \ No newline at end of file diff --git a/client/src/main/java/info/frostfs/sdk/FrostFSClient.java b/client/src/main/java/info/frostfs/sdk/FrostFSClient.java index 76d946f..3b4ecb5 100644 --- a/client/src/main/java/info/frostfs/sdk/FrostFSClient.java +++ b/client/src/main/java/info/frostfs/sdk/FrostFSClient.java @@ -18,8 +18,11 @@ import info.frostfs.sdk.jdo.NetworkSettings; import info.frostfs.sdk.jdo.PutObjectParameters; import info.frostfs.sdk.services.*; import info.frostfs.sdk.services.impl.*; +import info.frostfs.sdk.services.impl.interceptor.Configuration; +import info.frostfs.sdk.services.impl.interceptor.MonitoringClientInterceptor; import info.frostfs.sdk.utils.Validator; import io.grpc.Channel; +import io.grpc.ClientInterceptors; import java.util.List; @@ -40,6 +43,9 @@ public class FrostFSClient implements ContainerClient, ObjectClient, NetmapClien ? clientSettings.getChannel() : initGrpcChannel(clientSettings); + MonitoringClientInterceptor monitoringClientInterceptor = MonitoringClientInterceptor + .create(Configuration.allMetrics()); + channel = ClientInterceptors.intercept(channel, monitoringClientInterceptor); ClientEnvironment clientEnvironment = new ClientEnvironment(clientSettings.getKey(), channel, new Version(), this); diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/ClientMetrics.java b/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/ClientMetrics.java new file mode 100644 index 0000000..149c145 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/ClientMetrics.java @@ -0,0 +1,170 @@ +package info.frostfs.sdk.services.impl.interceptor; + +import io.grpc.Metadata; +import io.grpc.Status; +import io.prometheus.client.CollectorRegistry; +import io.prometheus.client.Counter; +import io.prometheus.client.Histogram; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +import static info.frostfs.sdk.services.impl.interceptor.Labels.*; + +public class ClientMetrics { + private static final List defaultRequestLabels = + Arrays.asList("grpc_type", "grpc_service", "grpc_method"); + + private static final List defaultResponseLabels = + Arrays.asList("grpc_type", "grpc_service", "grpc_method", "code", "grpc_code"); + + private static final Counter.Builder rpcStartedBuilder = + Counter.build() + .namespace("grpc") + .subsystem("client") + .name("started") + .help("Total number of RPCs started on the client."); + + private static final Counter.Builder rpcCompletedBuilder = + Counter.build() + .namespace("grpc") + .subsystem("client") + .name("completed") + .help("Total number of RPCs completed on the client, regardless of success or failure."); + + private static final Histogram.Builder completedLatencySecondsBuilder = + Histogram.build() + .namespace("grpc") + .subsystem("client") + .name("completed_latency_seconds") + .help("Histogram of rpc response latency (in seconds) for completed rpcs."); + + private static final Counter.Builder streamMessagesReceivedBuilder = + Counter.build() + .namespace("grpc") + .subsystem("client") + .name("msg_received") + .help("Total number of stream messages received from the server."); + + private static final Counter.Builder streamMessagesSentBuilder = + Counter.build() + .namespace("grpc") + .subsystem("client") + .name("msg_sent") + .help("Total number of stream messages sent by the client."); + + private final List> labelHeaderKeys; + private final Counter rpcStarted; + private final Counter rpcCompleted; + private final Counter streamMessagesReceived; + private final Counter streamMessagesSent; + private final Optional completedLatencySeconds; + + private final GrpcMethod method; + + private ClientMetrics( + List> labelHeaderKeys, + GrpcMethod method, + Counter rpcStarted, + Counter rpcCompleted, + Counter streamMessagesReceived, + Counter streamMessagesSent, + Optional completedLatencySeconds) { + this.labelHeaderKeys = labelHeaderKeys; + this.method = method; + this.rpcStarted = rpcStarted; + this.rpcCompleted = rpcCompleted; + this.streamMessagesReceived = streamMessagesReceived; + this.streamMessagesSent = streamMessagesSent; + this.completedLatencySeconds = completedLatencySeconds; + } + + public void recordCallStarted(Metadata metadata) { + addLabels(rpcStarted, customLabels(metadata, labelHeaderKeys), method).inc(); + } + + public void recordClientHandled(Status.Code code, Metadata metadata) { + List allLabels = new ArrayList<>(); + allLabels.add(code.toString()); + allLabels.add(code.toString()); + allLabels.addAll(customLabels(metadata, labelHeaderKeys)); + addLabels(rpcCompleted, allLabels, method).inc(); + } + + public void recordStreamMessageSent(Metadata metadata) { + addLabels(streamMessagesSent, customLabels(metadata, labelHeaderKeys), method).inc(); + } + + public void recordStreamMessageReceived(Metadata metadata) { + addLabels(streamMessagesReceived, customLabels(metadata, labelHeaderKeys), method).inc(); + } + + /** + * Only has any effect if monitoring is configured to include latency histograms. Otherwise, this + * does nothing. + */ + public void recordLatency(double latencySec, Metadata metadata) { + if (completedLatencySeconds.isEmpty()) { + return; + } + addLabels(completedLatencySeconds.get(), customLabels(metadata, labelHeaderKeys), method) + .observe(latencySec); + } + + /** Knows how to produce {@link ClientMetrics} instances for individual methods. */ + static class Factory { + private final List> labelHeaderKeys; + private final Counter rpcStarted; + private final Counter rpcCompleted; + private final Counter streamMessagesReceived; + private final Counter streamMessagesSent; + private final Optional completedLatencySeconds; + + Factory(Configuration configuration) { + CollectorRegistry registry = configuration.getCollectorRegistry(); + this.labelHeaderKeys = metadataKeys(configuration.getLabelHeaders()); + this.rpcStarted = + rpcStartedBuilder + .labelNames(asArray(defaultRequestLabels, configuration.getSanitizedLabelHeaders())) + .register(registry); + this.rpcCompleted = + rpcCompletedBuilder + .labelNames(asArray(defaultResponseLabels, configuration.getSanitizedLabelHeaders())) + .register(registry); + this.streamMessagesReceived = + streamMessagesReceivedBuilder + .labelNames(asArray(defaultRequestLabels, configuration.getSanitizedLabelHeaders())) + .register(registry); + this.streamMessagesSent = + streamMessagesSentBuilder + .labelNames(asArray(defaultRequestLabels, configuration.getSanitizedLabelHeaders())) + .register(registry); + + if (configuration.isIncludeLatencyHistograms()) { + this.completedLatencySeconds = + Optional.of( + ClientMetrics.completedLatencySecondsBuilder + .buckets(configuration.getLatencyBuckets()) + .labelNames( + asArray(defaultRequestLabels, configuration.getSanitizedLabelHeaders())) + .register(registry)); + } else { + this.completedLatencySeconds = Optional.empty(); + } + } + + /** Creates a {@link ClientMetrics} for the supplied gRPC method. */ + ClientMetrics createMetricsForMethod(GrpcMethod grpcMethod) { + return new ClientMetrics( + labelHeaderKeys, + grpcMethod, + rpcStarted, + rpcCompleted, + streamMessagesReceived, + streamMessagesSent, + completedLatencySeconds); + } + } +} diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/Configuration.java b/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/Configuration.java new file mode 100644 index 0000000..d4adcca --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/Configuration.java @@ -0,0 +1,153 @@ +package info.frostfs.sdk.services.impl.interceptor; + + +import io.prometheus.client.CollectorRegistry; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +public class Configuration { + private static final double[] DEFAULT_LATENCY_BUCKETS = + new double[] {.001, .005, .01, .05, 0.075, .1, .25, .5, 1, 2, 5, 10}; + + private final boolean isIncludeLatencyHistograms; + private final CollectorRegistry collectorRegistry; + private final double[] latencyBuckets; + private final List labelHeaders; + private final boolean isAddCodeLabelToHistograms; + private Configuration( + boolean isIncludeLatencyHistograms, + CollectorRegistry collectorRegistry, + double[] latencyBuckets, + List labelHeaders, + boolean isAddCodeLabelToHistograms) { + this.isIncludeLatencyHistograms = isIncludeLatencyHistograms; + this.collectorRegistry = collectorRegistry; + this.latencyBuckets = latencyBuckets; + this.labelHeaders = labelHeaders; + this.isAddCodeLabelToHistograms = isAddCodeLabelToHistograms; + } + + + /** Returns a {@link Configuration} for recording all cheap metrics about the rpcs. */ + public static Configuration cheapMetricsOnly() { + return new Configuration( + false /* isIncludeLatencyHistograms */, + CollectorRegistry.defaultRegistry, + DEFAULT_LATENCY_BUCKETS, + new ArrayList<>(), + false /* isAddCodeLabelToHistograms */); + } + + /** + * Returns a {@link Configuration} for recording all metrics about the rpcs. This includes metrics + * which might produce a lot of data, such as latency histograms. + */ + public static Configuration allMetrics() { + return new Configuration( + true /* isIncludeLatencyHistograms */, + CollectorRegistry.defaultRegistry, + DEFAULT_LATENCY_BUCKETS, + new ArrayList<>(), + false); + } + + /** + * Returns a copy {@link Configuration} with the difference that Prometheus metrics are recorded + * using the supplied {@link CollectorRegistry}. + */ + public Configuration withCollectorRegistry(CollectorRegistry collectorRegistry) { + return new Configuration( + isIncludeLatencyHistograms, + collectorRegistry, + latencyBuckets, + labelHeaders, + isAddCodeLabelToHistograms); + } + + /** + * Returns a copy {@link Configuration} with the difference that the latency histogram values are + * recorded with the specified set of buckets. + */ + public Configuration withLatencyBuckets(double[] buckets) { + return new Configuration( + isIncludeLatencyHistograms, + collectorRegistry, + buckets, + labelHeaders, + isAddCodeLabelToHistograms); + } + + /** + * Returns a copy {@link Configuration} that recognizes the given list of header names and uses + * their value from each request as prometheus labels. + * + *

Since hyphens is a common character in header names, and since Prometheus does not allow + * hyphens in label names, All hyphens in the list of header names will be converted to + * underscores before being added as metric label names. + * + *

If one of the headers added here is absent in one of the requests, its metric value for that + * request will be an empty string. + * + *

Example: {@code withLabelHeaders(Arrays.asList("User-Agent"))} will make all metrics carry a + * label "User_Agent", with label value filled in from the value of the "User-Agent" header of + * each request. + */ + public Configuration withLabelHeaders(List headers) { + List newHeaders = new ArrayList<>(labelHeaders); + newHeaders.addAll(headers); + return new Configuration( + isIncludeLatencyHistograms, + collectorRegistry, + latencyBuckets, + newHeaders, + isAddCodeLabelToHistograms); + } + + /** + * Returns a copy {@link Configuration} with the difference that status code label will be added + * to latency histogram. If latency histogram itself is disabled, this takes no effect. Warning: + * this will increase the number of histograms by a factor of actually happened codes (up to + * {@link io.grpc.Status.Code} values count), which could lead to additional local memory usage + * and load on prometheus (storage and memory usage, query-time complexity) + */ + public Configuration withCodeLabelInLatencyHistogram() { + return new Configuration( + isIncludeLatencyHistograms, + collectorRegistry, + latencyBuckets, + labelHeaders, + true /* isAddCodeLabelToHistograms */); + } + + /** Returns whether or not latency histograms for calls should be included. */ + public boolean isIncludeLatencyHistograms() { + return isIncludeLatencyHistograms; + } + + /** Returns the {@link CollectorRegistry} used to record stats. */ + public CollectorRegistry getCollectorRegistry() { + return collectorRegistry; + } + + /** Returns the histogram buckets to use for latency metrics. */ + public double[] getLatencyBuckets() { + return latencyBuckets; + } + + /** Returns the configured list of headers to be used as labels. */ + public List getLabelHeaders() { + return labelHeaders; + } + + /** Returns whether or not status code label should be added to latency histogram. */ + public boolean isAddCodeLabelToHistograms() { + return isAddCodeLabelToHistograms; + } + + /** + * Returns the sanitized version of the label headers, after turning all hyphens to underscores. + */ + public List getSanitizedLabelHeaders() { + return labelHeaders.stream().map(h -> h.replaceAll("-", "_")).collect(Collectors.toList()); + } +} diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/GrpcMethod.java b/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/GrpcMethod.java new file mode 100644 index 0000000..8f9aa4c --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/GrpcMethod.java @@ -0,0 +1,45 @@ +package info.frostfs.sdk.services.impl.interceptor; + +import io.grpc.MethodDescriptor; +import io.grpc.MethodDescriptor.MethodType; + +public class GrpcMethod { + private final String serviceName; + private final String methodName; + private final MethodType type; + + private GrpcMethod(String serviceName, String methodName, MethodType type) { + this.serviceName = serviceName; + this.methodName = methodName; + this.type = type; + } + + static GrpcMethod of(MethodDescriptor method) { + String serviceName = MethodDescriptor.extractFullServiceName(method.getFullMethodName()); + + // Full method names are of the form: "full.serviceName/MethodName". We extract the last part. + String methodName = method.getFullMethodName().substring(serviceName.length() + 1); + return new GrpcMethod(serviceName, methodName, method.getType()); + } + + + String serviceName() { + return serviceName; + } + + String methodName() { + return methodName; + } + + String type() { + return type.toString(); + } + + boolean streamsRequests() { + return type == MethodType.CLIENT_STREAMING || type == MethodType.BIDI_STREAMING; + } + + boolean streamsResponses() { + return type == MethodType.SERVER_STREAMING || type == MethodType.BIDI_STREAMING; + } +} diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/Labels.java b/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/Labels.java new file mode 100644 index 0000000..d026854 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/Labels.java @@ -0,0 +1,53 @@ +package info.frostfs.sdk.services.impl.interceptor; + +import io.grpc.Metadata; +import io.grpc.Metadata.Key; +import io.prometheus.client.SimpleCollector; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class Labels { + /** Merges two string lists into an array, maintaining order of first list then second list. */ + static String[] asArray(List firstList, List secondList) { + List list = new ArrayList<>(firstList); + list.addAll(secondList); + return list.toArray(new String[0]); + } + + /** Converts a list of strings to a list of grpc metadata keys. */ + static List> metadataKeys(List headerNames) { + List> keys = new ArrayList<>(); + for (String name : headerNames) { + keys.add(Key.of(name, Metadata.ASCII_STRING_MARSHALLER)); + } + return Collections.unmodifiableList(keys); + } + + /** + * Returns the ordered list of custom label values, by looking into metadata for values of + * selected custom headers. + */ + static List customLabels(Metadata metadata, List> labelHeaderKeys) { + List labels = new ArrayList<>(); + for (Key key : labelHeaderKeys) { + if (metadata.containsKey(key)) { + labels.add(metadata.get(key)); + } else { + labels.add(""); + } + } + return Collections.unmodifiableList(labels); + } + + /** Adds standard labels, as well as custom ones, in order, to a given collector. */ + static T addLabels(SimpleCollector collector, List labels, GrpcMethod method) { + List allLabels = new ArrayList<>(); + allLabels.add(method.type()); + allLabels.add(method.serviceName()); + allLabels.add(method.methodName()); + allLabels.addAll(labels); + return collector.labels(allLabels.toArray(new String[0])); + } +} diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/MonitoringClientCall.java b/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/MonitoringClientCall.java new file mode 100644 index 0000000..c116ca7 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/MonitoringClientCall.java @@ -0,0 +1,47 @@ +package info.frostfs.sdk.services.impl.interceptor; + +import io.grpc.ClientCall; +import io.grpc.ForwardingClientCall; +import io.grpc.Metadata; + +import java.time.Clock; + +public class MonitoringClientCall extends ForwardingClientCall.SimpleForwardingClientCall { + private final ClientMetrics clientMetrics; + private final GrpcMethod grpcMethod; + private final Configuration configuration; + private final Clock clock; + private Metadata requestMetadata; + + MonitoringClientCall( + ClientCall delegate, + ClientMetrics clientMetrics, + GrpcMethod grpcMethod, + Configuration configuration, + Clock clock) { + super(delegate); + this.clientMetrics = clientMetrics; + this.grpcMethod = grpcMethod; + this.configuration = configuration; + this.clock = clock; + } + + @Override + public void start(Listener delegate, Metadata metadata) { + this.requestMetadata = metadata; + clientMetrics.recordCallStarted(metadata); + super.start( + new MonitoringClientCallListener<>( + delegate, clientMetrics, grpcMethod, configuration, clock, metadata), + metadata); + } + + @Override + public void sendMessage(R requestMessage) { + if (grpcMethod.streamsRequests()) { + clientMetrics.recordStreamMessageSent( + requestMetadata == null ? new Metadata() : requestMetadata); + } + super.sendMessage(requestMessage); + } +} diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/MonitoringClientCallListener.java b/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/MonitoringClientCallListener.java new file mode 100644 index 0000000..e8c9caf --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/MonitoringClientCallListener.java @@ -0,0 +1,61 @@ +package info.frostfs.sdk.services.impl.interceptor; + +import io.grpc.ClientCall; +import io.grpc.ForwardingClientCallListener; +import io.grpc.Metadata; +import io.grpc.Status; + +import java.time.Clock; +import java.time.Instant; + +public class MonitoringClientCallListener extends ForwardingClientCallListener { + private static final long MILLIS_PER_SECOND = 1000L; + + private final ClientCall.Listener delegate; + private final ClientMetrics clientMetrics; + private final GrpcMethod grpcMethod; + private final Configuration configuration; + private final Clock clock; + private final Instant startInstant; + private final Metadata requestMetadata; + + MonitoringClientCallListener( + ClientCall.Listener delegate, + ClientMetrics clientMetrics, + GrpcMethod grpcMethod, + Configuration configuration, + Clock clock, + Metadata requestMetadata) { + this.delegate = delegate; + this.clientMetrics = clientMetrics; + this.grpcMethod = grpcMethod; + this.configuration = configuration; + this.clock = clock; + this.startInstant = clock.instant(); + this.requestMetadata = requestMetadata; + } + + @Override + protected ClientCall.Listener delegate() { + return delegate; + } + + @Override + public void onClose(Status status, Metadata metadata) { + clientMetrics.recordClientHandled(status.getCode(), requestMetadata); + if (configuration.isIncludeLatencyHistograms()) { + double latencySec = + (clock.millis() - startInstant.toEpochMilli()) / (double) MILLIS_PER_SECOND; + clientMetrics.recordLatency(latencySec, requestMetadata); + } + super.onClose(status, metadata); + } + + @Override + public void onMessage(S responseMessage) { + if (grpcMethod.streamsResponses()) { + clientMetrics.recordStreamMessageReceived(requestMetadata); + } + super.onMessage(responseMessage); + } +} diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/MonitoringClientInterceptor.java b/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/MonitoringClientInterceptor.java new file mode 100644 index 0000000..ada4761 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/MonitoringClientInterceptor.java @@ -0,0 +1,35 @@ +package info.frostfs.sdk.services.impl.interceptor; + +import java.time.Clock; +import io.grpc.CallOptions; +import io.grpc.Channel; +import io.grpc.ClientCall; +import io.grpc.ClientInterceptor; +import io.grpc.MethodDescriptor; + + +public class MonitoringClientInterceptor implements ClientInterceptor { + private final Clock clock; + private final Configuration configuration; + private final ClientMetrics.Factory clientMetricsFactory; + + private MonitoringClientInterceptor( + Clock clock, Configuration configuration, ClientMetrics.Factory clientMetricsFactory) { + this.clock = clock; + this.configuration = configuration; + this.clientMetricsFactory = clientMetricsFactory; + } + public static MonitoringClientInterceptor create(Configuration configuration) { + return new MonitoringClientInterceptor( + Clock.systemDefaultZone(), configuration, new ClientMetrics.Factory(configuration)); + } + + @Override + public ClientCall interceptCall( + MethodDescriptor methodDescriptor, CallOptions callOptions, Channel channel) { + GrpcMethod grpcMethod = GrpcMethod.of(methodDescriptor); + ClientMetrics metrics = clientMetricsFactory.createMetricsForMethod(grpcMethod); + return new MonitoringClientCall<>( + channel.newCall(methodDescriptor, callOptions), metrics, grpcMethod, configuration, clock); + } +} diff --git a/cryptography/src/main/java/info/frostfs/sdk/Base58.java b/cryptography/src/main/java/info/frostfs/sdk/Base58.java index 477412b..d302763 100644 --- a/cryptography/src/main/java/info/frostfs/sdk/Base58.java +++ b/cryptography/src/main/java/info/frostfs/sdk/Base58.java @@ -58,8 +58,7 @@ public class Base58 { byte[] checksum = getSha256(getSha256(data)); var buffer = concat(data, Arrays.copyOfRange(checksum, 0, 4)); - var ret = encode(buffer); - return ret; + return encode(buffer); } public static String encode(byte[] input) { From 3be4e87bc188da2cfdcbf43ed4febd695db4c0f8 Mon Sep 17 00:00:00 2001 From: Pavel Pogodaev Date: Fri, 18 Oct 2024 13:40:52 +0300 Subject: [PATCH 20/38] [#3] Move client to root target - ci/cd optimization Signed-off-by: Pavel Pogodaev --- pom.xml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/pom.xml b/pom.xml index ed517d6..0f6fc39 100644 --- a/pom.xml +++ b/pom.xml @@ -85,6 +85,32 @@ 3.4.0 + + + org.apache.maven.plugins + maven-resources-plugin + 3.3.0 + + + copy-resources + package + + copy-resources + + + ../target/ + + + ${basedir}/target + + /client* + + + + + + + \ No newline at end of file From 64e275713f1c92d6415d588a57ccf086a58d5d9c Mon Sep 17 00:00:00 2001 From: Bruk Ori Date: Tue, 29 Oct 2024 13:18:18 +0300 Subject: [PATCH 21/38] [#21] add forgejo maven verify action Signed-off-by: Ori Bruk --- .forgejo/workflows/verify-code.yaml | 12 ++++++++++++ pom.xml | 11 ++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 .forgejo/workflows/verify-code.yaml diff --git a/.forgejo/workflows/verify-code.yaml b/.forgejo/workflows/verify-code.yaml new file mode 100644 index 0000000..bb54d65 --- /dev/null +++ b/.forgejo/workflows/verify-code.yaml @@ -0,0 +1,12 @@ +name: Verify code phase +on: [pull_request] + +jobs: + verify-code: + name: Verify code + runs-on: docker + container: git.frostfs.info/truecloudlab/env:openjdk-11-maven-3.8.6 + steps: + - uses: actions/checkout@v3 + - name: Run the Maven verify phase + run: mvn --batch-mode --update-snapshots verify diff --git a/pom.xml b/pom.xml index 0f6fc39..b8d61db 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,16 @@ 3.4.0 - + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.0 + + + org.apache.maven.plugins + maven-failsafe-plugin + 2.22.0 + org.apache.maven.plugins maven-resources-plugin From 694bb963e402d2900ca16dd427cee637e90c42ef Mon Sep 17 00:00:00 2001 From: Bruk Ori Date: Fri, 1 Nov 2024 01:06:18 +0300 Subject: [PATCH 22/38] [#23] Update proto api Add chain functionality Add tests Signed-off-by: Ori Bruk --- .../java/info/frostfs/sdk/FrostFSClient.java | 29 ++- .../sdk/services/ApeManagerClient.java | 14 ++ .../services/impl/ApeManagerClientImpl.java | 134 +++++++++++ .../test/java/info/frostfs/sdk/FileUtils.java | 31 +++ .../sdk/services/ApeManagerClientTest.java | 216 ++++++++++++++++++ .../testgenerator/ApeManagerGenerator.java | 124 ++++++++++ .../sdk/testgenerator/RefsGenerator.java | 65 ++++++ .../sdk/testgenerator/SessionGenerator.java | 178 +++++++++++++++ .../sdk/testgenerator/StatusGenerator.java | 29 +++ client/src/test/resources/test_chain_raw.json | 30 +++ .../main/java/info/frostfs/sdk/Helper.java | 10 + .../java/info/frostfs/sdk/HelperTest.java | 19 ++ .../info/frostfs/sdk/dto/chain/Chain.java | 10 + .../frostfs/sdk/dto/chain/ChainTarget.java | 17 ++ .../info/frostfs/sdk/enums/TargetType.java | 34 +++ .../sdk/mappers/chain/ChainMapper.java | 31 +++ .../sdk/mappers/chain/ChainTargetMapper.java | 33 +++ .../sdk/mappers/chain/ChainMapperTest.java | 64 ++++++ .../mappers/chain/ChainTargetMapperTest.java | 52 +++++ .../object/ObjectFilterMapperTest.java | 2 +- pom.xml | 8 +- .../src/main/proto/accounting/service.proto | 19 +- protos/src/main/proto/accounting/types.proto | 3 +- protos/src/main/proto/acl/types.proto | 29 ++- .../proto/{apemanager => ape}/types.proto | 5 +- .../src/main/proto/apemanager/service.proto | 19 +- protos/src/main/proto/container/service.proto | 205 +---------------- protos/src/main/proto/container/types.proto | 3 +- protos/src/main/proto/lock/types.proto | 1 - protos/src/main/proto/netmap/service.proto | 9 +- protos/src/main/proto/netmap/types.proto | 75 ++++-- protos/src/main/proto/object/service.proto | 142 +++++++++++- protos/src/main/proto/object/types.proto | 17 +- protos/src/main/proto/refs/types.proto | 12 +- protos/src/main/proto/session/service.proto | 3 +- protos/src/main/proto/session/types.proto | 12 +- protos/src/main/proto/status/types.proto | 22 +- protos/src/main/proto/tombstone/types.proto | 7 +- 38 files changed, 1414 insertions(+), 299 deletions(-) create mode 100644 client/src/main/java/info/frostfs/sdk/services/ApeManagerClient.java create mode 100644 client/src/main/java/info/frostfs/sdk/services/impl/ApeManagerClientImpl.java create mode 100644 client/src/test/java/info/frostfs/sdk/FileUtils.java create mode 100644 client/src/test/java/info/frostfs/sdk/services/ApeManagerClientTest.java create mode 100644 client/src/test/java/info/frostfs/sdk/testgenerator/ApeManagerGenerator.java create mode 100644 client/src/test/java/info/frostfs/sdk/testgenerator/RefsGenerator.java create mode 100644 client/src/test/java/info/frostfs/sdk/testgenerator/SessionGenerator.java create mode 100644 client/src/test/java/info/frostfs/sdk/testgenerator/StatusGenerator.java create mode 100644 client/src/test/resources/test_chain_raw.json create mode 100644 models/src/main/java/info/frostfs/sdk/dto/chain/Chain.java create mode 100644 models/src/main/java/info/frostfs/sdk/dto/chain/ChainTarget.java create mode 100644 models/src/main/java/info/frostfs/sdk/enums/TargetType.java create mode 100644 models/src/main/java/info/frostfs/sdk/mappers/chain/ChainMapper.java create mode 100644 models/src/main/java/info/frostfs/sdk/mappers/chain/ChainTargetMapper.java create mode 100644 models/src/test/java/info/frostfs/sdk/mappers/chain/ChainMapperTest.java create mode 100644 models/src/test/java/info/frostfs/sdk/mappers/chain/ChainTargetMapperTest.java rename protos/src/main/proto/{apemanager => ape}/types.proto (73%) diff --git a/client/src/main/java/info/frostfs/sdk/FrostFSClient.java b/client/src/main/java/info/frostfs/sdk/FrostFSClient.java index 3b4ecb5..634c956 100644 --- a/client/src/main/java/info/frostfs/sdk/FrostFSClient.java +++ b/client/src/main/java/info/frostfs/sdk/FrostFSClient.java @@ -1,6 +1,7 @@ package info.frostfs.sdk; -import frostfs.session.Types; +import info.frostfs.sdk.dto.chain.Chain; +import info.frostfs.sdk.dto.chain.ChainTarget; import info.frostfs.sdk.dto.container.Container; import info.frostfs.sdk.dto.container.ContainerId; import info.frostfs.sdk.dto.netmap.NetmapSnapshot; @@ -30,10 +31,12 @@ import static info.frostfs.sdk.constants.ErrorConst.VERSION_UNSUPPORTED_TEMPLATE import static info.frostfs.sdk.tools.GrpcClient.initGrpcChannel; import static java.util.Objects.nonNull; -public class FrostFSClient implements ContainerClient, ObjectClient, NetmapClient, SessionClient, ToolsClient { +public class FrostFSClient + implements ContainerClient, ObjectClient, ApeManagerClient, NetmapClient, SessionClient, ToolsClient { private final ContainerClientImpl containerClientImpl; - private final NetmapClientImpl netmapClientImpl; private final ObjectClientImpl objectClientImpl; + private final ApeManagerClientImpl apeManagerClient; + private final NetmapClientImpl netmapClientImpl; private final SessionClientImpl sessionClientImpl; private final ObjectToolsImpl objectToolsImpl; @@ -52,9 +55,10 @@ public class FrostFSClient implements ContainerClient, ObjectClient, NetmapClien Validator.validate(clientEnvironment); this.containerClientImpl = new ContainerClientImpl(clientEnvironment); + this.objectClientImpl = new ObjectClientImpl(clientEnvironment); + this.apeManagerClient = new ApeManagerClientImpl(clientEnvironment); this.netmapClientImpl = new NetmapClientImpl(clientEnvironment); this.sessionClientImpl = new SessionClientImpl(clientEnvironment); - this.objectClientImpl = new ObjectClientImpl(clientEnvironment); this.objectToolsImpl = new ObjectToolsImpl(clientEnvironment); checkFrostFsVersionSupport(clientEnvironment.getVersion()); } @@ -118,6 +122,21 @@ public class FrostFSClient implements ContainerClient, ObjectClient, NetmapClien return objectClientImpl.searchObjects(cid, filters); } + @Override + public byte[] addChain(Chain chain, ChainTarget chainTarget) { + return apeManagerClient.addChain(chain, chainTarget); + } + + @Override + public void removeChain(Chain chain, ChainTarget chainTarget) { + apeManagerClient.removeChain(chain, chainTarget); + } + + @Override + public List listChains(ChainTarget chainTarget) { + return apeManagerClient.listChains(chainTarget); + } + @Override public NetmapSnapshot getNetmapSnapshot() { return netmapClientImpl.getNetmapSnapshot(); @@ -138,7 +157,7 @@ public class FrostFSClient implements ContainerClient, ObjectClient, NetmapClien return sessionClientImpl.createSession(expiration); } - public Types.SessionToken createSessionInternal(long expiration) { + public frostfs.session.Types.SessionToken createSessionInternal(long expiration) { return sessionClientImpl.createSessionInternal(expiration); } diff --git a/client/src/main/java/info/frostfs/sdk/services/ApeManagerClient.java b/client/src/main/java/info/frostfs/sdk/services/ApeManagerClient.java new file mode 100644 index 0000000..37e4033 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/services/ApeManagerClient.java @@ -0,0 +1,14 @@ +package info.frostfs.sdk.services; + +import info.frostfs.sdk.dto.chain.Chain; +import info.frostfs.sdk.dto.chain.ChainTarget; + +import java.util.List; + +public interface ApeManagerClient { + byte[] addChain(Chain chain, ChainTarget chainTarget); + + void removeChain(Chain chain, ChainTarget chainTarget); + + List listChains(ChainTarget chainTarget); +} \ No newline at end of file diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/ApeManagerClientImpl.java b/client/src/main/java/info/frostfs/sdk/services/impl/ApeManagerClientImpl.java new file mode 100644 index 0000000..0e5c9f1 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/services/impl/ApeManagerClientImpl.java @@ -0,0 +1,134 @@ +package info.frostfs.sdk.services.impl; + +import com.google.protobuf.ByteString; +import frostfs.ape.Types; +import frostfs.apemanager.APEManagerServiceGrpc; +import frostfs.apemanager.Service; +import info.frostfs.sdk.dto.chain.Chain; +import info.frostfs.sdk.dto.chain.ChainTarget; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; +import info.frostfs.sdk.jdo.ClientEnvironment; +import info.frostfs.sdk.mappers.chain.ChainMapper; +import info.frostfs.sdk.mappers.chain.ChainTargetMapper; +import info.frostfs.sdk.services.ApeManagerClient; +import info.frostfs.sdk.services.ContextAccessor; +import info.frostfs.sdk.tools.RequestConstructor; +import info.frostfs.sdk.tools.RequestSigner; +import info.frostfs.sdk.tools.Verifier; + +import java.util.List; +import java.util.Map; + +import static info.frostfs.sdk.constants.ErrorConst.*; +import static java.util.Objects.isNull; + +public class ApeManagerClientImpl extends ContextAccessor implements ApeManagerClient { + private final APEManagerServiceGrpc.APEManagerServiceBlockingStub apeManagerServiceClient; + + public ApeManagerClientImpl(ClientEnvironment clientEnvironment) { + super(clientEnvironment); + this.apeManagerServiceClient = APEManagerServiceGrpc.newBlockingStub(getContext().getChannel()); + } + + @Override + public byte[] addChain(Chain chain, ChainTarget chainTarget) { + if (isNull(chain) || isNull(chainTarget)) { + throw new ValidationFrostFSException( + String.format( + PARAMS_ARE_MISSING_TEMPLATE, + String.join(FIELDS_DELIMITER_COMMA, Chain.class.getName(), ChainTarget.class.getName()) + ) + ); + } + + var request = createAddChainRequest(chain, chainTarget, null); + + var response = apeManagerServiceClient.addChain(request); + + Verifier.checkResponse(response); + + return response.getBody().getChainId().toByteArray(); + } + + @Override + public void removeChain(Chain chain, ChainTarget chainTarget) { + if (isNull(chain) || isNull(chainTarget)) { + throw new ValidationFrostFSException( + String.format( + PARAMS_ARE_MISSING_TEMPLATE, + String.join(FIELDS_DELIMITER_COMMA, Chain.class.getName(), ChainTarget.class.getName()) + ) + ); + } + + var request = createRemoveChainRequest(chain, chainTarget, null); + + var response = apeManagerServiceClient.removeChain(request); + + Verifier.checkResponse(response); + } + + @Override + public List listChains(ChainTarget chainTarget) { + if (isNull(chainTarget)) { + throw new ValidationFrostFSException(String.format(PARAM_IS_MISSING_TEMPLATE, ChainTarget.class.getName())); + } + + var request = createListChainsRequest(chainTarget, null); + + var response = apeManagerServiceClient.listChains(request); + + Verifier.checkResponse(response); + + return ChainMapper.toModels(response.getBody().getChainsList()); + } + + private Service.AddChainRequest createAddChainRequest(Chain chain, + ChainTarget chainTarget, + Map xHeaders) { + var chainGrpc = Types.Chain.newBuilder() + .setRaw(ByteString.copyFrom(chain.getRaw())) + .build(); + var body = Service.AddChainRequest.Body.newBuilder() + .setChain(chainGrpc) + .setTarget(ChainTargetMapper.toGrpcMessage(chainTarget)) + .build(); + var request = Service.AddChainRequest.newBuilder() + .setBody(body); + + RequestConstructor.addMetaHeader(request, xHeaders); + RequestSigner.sign(request, getContext().getKey()); + + return request.build(); + } + + private Service.RemoveChainRequest createRemoveChainRequest(Chain chain, + ChainTarget chainTarget, + Map xHeaders) { + var body = Service.RemoveChainRequest.Body.newBuilder() + .setChainId(ByteString.copyFrom(chain.getRaw())) + .setTarget(ChainTargetMapper.toGrpcMessage(chainTarget)) + .build(); + var request = Service.RemoveChainRequest.newBuilder() + .setBody(body); + + RequestConstructor.addMetaHeader(request, xHeaders); + RequestSigner.sign(request, getContext().getKey()); + + return request.build(); + } + + private Service.ListChainsRequest createListChainsRequest(ChainTarget chainTarget, + Map xHeaders) { + var body = Service.ListChainsRequest.Body.newBuilder() + .setTarget(ChainTargetMapper.toGrpcMessage(chainTarget)) + .build(); + var request = Service.ListChainsRequest.newBuilder() + .setBody(body); + + RequestConstructor.addMetaHeader(request, xHeaders); + RequestSigner.sign(request, getContext().getKey()); + + return request.build(); + } +} diff --git a/client/src/test/java/info/frostfs/sdk/FileUtils.java b/client/src/test/java/info/frostfs/sdk/FileUtils.java new file mode 100644 index 0000000..0eb9e80 --- /dev/null +++ b/client/src/test/java/info/frostfs/sdk/FileUtils.java @@ -0,0 +1,31 @@ +package info.frostfs.sdk; + +import com.google.common.io.ByteStreams; +import lombok.SneakyThrows; +import org.apache.commons.lang3.StringUtils; + +import java.io.InputStream; + +import static java.util.Objects.isNull; + +public class FileUtils { + + @SneakyThrows + public static byte[] resourceToBytes(String resourcePath) { + if (StringUtils.isBlank(resourcePath)) { + throw new IllegalArgumentException("Blank filename!"); + } + + ClassLoader loader = FileUtils.class.getClassLoader(); + if (isNull(loader)) { + throw new RuntimeException("Class loader is null!"); + } + + InputStream certStream = loader.getResourceAsStream(resourcePath); + if (isNull(certStream)) { + throw new RuntimeException("Resource could not be found!"); + } + + return ByteStreams.toByteArray(certStream); + } +} diff --git a/client/src/test/java/info/frostfs/sdk/services/ApeManagerClientTest.java b/client/src/test/java/info/frostfs/sdk/services/ApeManagerClientTest.java new file mode 100644 index 0000000..7cdfcff --- /dev/null +++ b/client/src/test/java/info/frostfs/sdk/services/ApeManagerClientTest.java @@ -0,0 +1,216 @@ +package info.frostfs.sdk.services; + +import frostfs.apemanager.APEManagerServiceGrpc; +import frostfs.apemanager.Service; +import info.frostfs.sdk.FileUtils; +import info.frostfs.sdk.dto.chain.Chain; +import info.frostfs.sdk.dto.chain.ChainTarget; +import info.frostfs.sdk.enums.TargetType; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; +import info.frostfs.sdk.jdo.ClientEnvironment; +import info.frostfs.sdk.services.impl.ApeManagerClientImpl; +import info.frostfs.sdk.testgenerator.ApeManagerGenerator; +import info.frostfs.sdk.tools.RequestConstructor; +import info.frostfs.sdk.tools.RequestSigner; +import info.frostfs.sdk.tools.Verifier; +import io.grpc.Channel; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.platform.commons.util.ReflectionUtils; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.lang.reflect.Field; +import java.util.stream.Collectors; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class ApeManagerClientTest { + private ApeManagerClientImpl apeManagerClient; + + @Mock + private APEManagerServiceGrpc.APEManagerServiceBlockingStub apeManagerServiceClient; + @Mock + private ClientEnvironment clientEnvironment; + @Mock + private Channel channel; + + private MockedStatic verifierMock; + private MockedStatic requestConstructorMock; + private MockedStatic requestSignerMock; + + @BeforeEach + void setUp() throws IllegalAccessException { + when(clientEnvironment.getChannel()).thenReturn(channel); + apeManagerClient = new ApeManagerClientImpl(clientEnvironment); + + Field field = ReflectionUtils.findFields(ApeManagerClientImpl.class, + f -> f.getName().equals("apeManagerServiceClient"), + ReflectionUtils.HierarchyTraversalMode.TOP_DOWN) + .get(0); + + field.setAccessible(true); + field.set(apeManagerClient, apeManagerServiceClient); + + verifierMock = Mockito.mockStatic(Verifier.class); + requestConstructorMock = Mockito.mockStatic(RequestConstructor.class); + requestSignerMock = Mockito.mockStatic(RequestSigner.class); + } + + @AfterEach + void cleanUp() { + verifierMock.close(); + requestConstructorMock.close(); + requestSignerMock.close(); + } + + @Test + void addChain_success() { + //Given + Chain chain = generateChain(); + ChainTarget chainTarget = generateChainTarget(); + + var response = ApeManagerGenerator.generateAddChainResponse(); + + var captor = ArgumentCaptor.forClass(Service.AddChainRequest.class); + + when(apeManagerServiceClient.addChain(captor.capture())).thenReturn(response); + + //When + var result = apeManagerClient.addChain(chain, chainTarget); + + //Then + requestConstructorMock.verify( + () -> RequestConstructor.addMetaHeader(any(Service.AddChainRequest.Builder.class), eq(null)), + times(1) + ); + requestSignerMock.verify( + () -> RequestSigner.sign(any(Service.AddChainRequest.Builder.class), eq(null)), + times(1) + ); + verifierMock.verify(() -> Verifier.checkResponse(response), times(1)); + + assertThat(result).containsOnly(response.getBody().getChainId().toByteArray()); + + var request = captor.getValue(); + assertThat(request.getBody().getChain().getRaw().toByteArray()).containsOnly(chain.getRaw()); + assertEquals(chainTarget.getName(), request.getBody().getTarget().getName()); + assertEquals(chainTarget.getType().value, request.getBody().getTarget().getType().getNumber()); + } + + @Test + void addChain_wrongParams() { + //Given + Chain chain = generateChain(); + ChainTarget chainTarget = generateChainTarget(); + + //When + Then + assertThrows(ValidationFrostFSException.class, () -> apeManagerClient.addChain(null, chainTarget)); + assertThrows(ValidationFrostFSException.class, () -> apeManagerClient.addChain(chain, null)); + assertThrows(ValidationFrostFSException.class, () -> apeManagerClient.addChain(null, null)); + } + + @Test + void removeChain_success() { + //Given + Chain chain = generateChain(); + ChainTarget chainTarget = generateChainTarget(); + + var response = ApeManagerGenerator.generateRemoveChainResponse(); + + var captor = ArgumentCaptor.forClass(Service.RemoveChainRequest.class); + + when(apeManagerServiceClient.removeChain(captor.capture())).thenReturn(response); + + //When + apeManagerClient.removeChain(chain, chainTarget); + + //Then + requestConstructorMock.verify( + () -> RequestConstructor.addMetaHeader(any(Service.RemoveChainRequest.Builder.class), eq(null)), + times(1) + ); + requestSignerMock.verify( + () -> RequestSigner.sign(any(Service.RemoveChainRequest.Builder.class), eq(null)), + times(1) + ); + verifierMock.verify(() -> Verifier.checkResponse(response), times(1)); + + var request = captor.getValue(); + assertThat(request.getBody().getChainId().toByteArray()).containsOnly(chain.getRaw()); + assertEquals(chainTarget.getName(), request.getBody().getTarget().getName()); + assertEquals(chainTarget.getType().value, request.getBody().getTarget().getType().getNumber()); + } + + @Test + void removeChain_wrongParams() { + //Given + Chain chain = generateChain(); + ChainTarget chainTarget = generateChainTarget(); + + //When + Then + assertThrows(ValidationFrostFSException.class, () -> apeManagerClient.removeChain(null, chainTarget)); + assertThrows(ValidationFrostFSException.class, () -> apeManagerClient.removeChain(chain, null)); + assertThrows(ValidationFrostFSException.class, () -> apeManagerClient.removeChain(null, null)); + } + + @Test + void listChain_success() { + //Given + ChainTarget chainTarget = generateChainTarget(); + + var response = ApeManagerGenerator.generateListChainsResponse(); + + var captor = ArgumentCaptor.forClass(Service.ListChainsRequest.class); + + when(apeManagerServiceClient.listChains(captor.capture())).thenReturn(response); + + //When + var result = apeManagerClient.listChains(chainTarget); + + //Then + requestConstructorMock.verify( + () -> RequestConstructor.addMetaHeader(any(Service.ListChainsRequest.Builder.class), eq(null)), + times(1) + ); + requestSignerMock.verify( + () -> RequestSigner.sign(any(Service.ListChainsRequest.Builder.class), eq(null)), + times(1) + ); + verifierMock.verify(() -> Verifier.checkResponse(response), times(1)); + + var actual = result.stream().map(Chain::getRaw).collect(Collectors.toList()); + var expected = response.getBody().getChainsList().stream() + .map(chain -> chain.getRaw().toByteArray()) + .collect(Collectors.toList()); + assertThat(actual).hasSize(10).containsAll(expected); + + var request = captor.getValue(); + assertEquals(chainTarget.getName(), request.getBody().getTarget().getName()); + assertEquals(chainTarget.getType().value, request.getBody().getTarget().getType().getNumber()); + } + + @Test + void listChain_wrongParams() { + //When + Then + assertThrows(ValidationFrostFSException.class, () -> apeManagerClient.listChains(null)); + } + + private Chain generateChain() { + byte[] chainRaw = FileUtils.resourceToBytes("test_chain_raw.json"); + return new Chain(chainRaw); + } + + private ChainTarget generateChainTarget() { + return new ChainTarget("BzQw5HH3feoxFDD5tCT87Y1726qzgLfxEE7wgtoRzB3R", TargetType.NAMESPACE); + } +} diff --git a/client/src/test/java/info/frostfs/sdk/testgenerator/ApeManagerGenerator.java b/client/src/test/java/info/frostfs/sdk/testgenerator/ApeManagerGenerator.java new file mode 100644 index 0000000..1a9e497 --- /dev/null +++ b/client/src/test/java/info/frostfs/sdk/testgenerator/ApeManagerGenerator.java @@ -0,0 +1,124 @@ +package info.frostfs.sdk.testgenerator; + +import com.google.protobuf.ByteString; +import frostfs.ape.Types; +import frostfs.apemanager.Service; +import info.frostfs.sdk.FileUtils; +import info.frostfs.sdk.Helper; + +import java.util.ArrayList; + +public class ApeManagerGenerator { + + private static ByteString generateChainID() { + return ByteString.copyFrom(Helper.getByteArrayFromHex("616c6c6f774f626a476574436e72")); + } + + private static Types.Chain generateRawChain() { + byte[] chainRaw = FileUtils.resourceToBytes("test_chain_raw.json"); + + return Types.Chain.newBuilder() + .setRaw(ByteString.copyFrom(chainRaw)) + .build(); + } + + private static Iterable generateRawChains(int size) { + var list = new ArrayList(size); + for (int i = 0; i < size; i++) { + list.add(generateRawChain()); + } + + return list; + } + + private static Types.ChainTarget generateChainTarget() { + return Types.ChainTarget.newBuilder() + .setType(Types.TargetType.CONTAINER) + .setName("BzQw5HH3feoxFDD5tCT87Y1726qzgLfxEE7wgtoRzB3R") + .build(); + } + + public static Service.AddChainRequest.Body generateAddChainRequestBody() { + return Service.AddChainRequest.Body.newBuilder() + .setTarget(generateChainTarget()) + .setChain(generateRawChain()) + .build(); + } + + public static Service.AddChainRequest generateAddChainRequest() { + return Service.AddChainRequest.newBuilder() + .setBody(generateAddChainRequestBody()) + .setMetaHeader(SessionGenerator.generateRequestMetaHeader()) + .setVerifyHeader(SessionGenerator.generateRequestVerificationHeader()) + .build(); + } + + public static Service.AddChainResponse.Body generateAddChainResponseBody() { + return Service.AddChainResponse.Body.newBuilder() + .setChainId(generateChainID()) + .build(); + } + + public static Service.AddChainResponse generateAddChainResponse() { + return Service.AddChainResponse.newBuilder() + .setBody(generateAddChainResponseBody()) + .setMetaHeader(SessionGenerator.generateResponseMetaHeader()) + .setVerifyHeader(SessionGenerator.generateResponseVerificationHeader()) + .build(); + } + + public static Service.RemoveChainRequest.Body generateRemoveChainRequestBody() { + return Service.RemoveChainRequest.Body.newBuilder() + .setChainId(generateChainID()) + .setTarget(generateChainTarget()) + .build(); + } + + public static Service.RemoveChainRequest generateRemoveChainRequest() { + return Service.RemoveChainRequest.newBuilder() + .setBody(generateRemoveChainRequestBody()) + .setMetaHeader(SessionGenerator.generateRequestMetaHeader()) + .setVerifyHeader(SessionGenerator.generateRequestVerificationHeader()) + .build(); + } + + public static Service.RemoveChainResponse.Body generateRemoveChainResponseBody() { + return Service.RemoveChainResponse.Body.getDefaultInstance(); + } + + public static Service.RemoveChainResponse generateRemoveChainResponse() { + return Service.RemoveChainResponse.newBuilder() + .setBody(generateRemoveChainResponseBody()) + .setMetaHeader(SessionGenerator.generateResponseMetaHeader()) + .setVerifyHeader(SessionGenerator.generateResponseVerificationHeader()) + .build(); + } + + public static Service.ListChainsRequest.Body generateListChainsRequestBody() { + return Service.ListChainsRequest.Body.newBuilder() + .setTarget(generateChainTarget()) + .build(); + } + + public static Service.ListChainsRequest generateListChainsRequest() { + return Service.ListChainsRequest.newBuilder() + .setBody(generateListChainsRequestBody()) + .setMetaHeader(SessionGenerator.generateRequestMetaHeader()) + .setVerifyHeader(SessionGenerator.generateRequestVerificationHeader()) + .build(); + } + + public static Service.ListChainsResponse.Body generateListChainsResponseBody() { + return Service.ListChainsResponse.Body.newBuilder() + .addAllChains(generateRawChains(10)) + .build(); + } + + public static Service.ListChainsResponse generateListChainsResponse() { + return Service.ListChainsResponse.newBuilder() + .setBody(generateListChainsResponseBody()) + .setMetaHeader(SessionGenerator.generateResponseMetaHeader()) + .setVerifyHeader(SessionGenerator.generateResponseVerificationHeader()) + .build(); + } +} diff --git a/client/src/test/java/info/frostfs/sdk/testgenerator/RefsGenerator.java b/client/src/test/java/info/frostfs/sdk/testgenerator/RefsGenerator.java new file mode 100644 index 0000000..c91e1f7 --- /dev/null +++ b/client/src/test/java/info/frostfs/sdk/testgenerator/RefsGenerator.java @@ -0,0 +1,65 @@ +package info.frostfs.sdk.testgenerator; + +import com.google.protobuf.ByteString; +import frostfs.refs.Types; + +import java.util.Arrays; +import java.util.Random; + +public class RefsGenerator { + + public static Types.Version generateVersion() { + return Types.Version.newBuilder() + .setMajor(2) + .setMinor(1) + .build(); + } + + public static Types.OwnerID generateOwnerID() { + return Types.OwnerID.newBuilder() + .setValue(ByteString.copyFrom(new byte[]{1, 2, 3})) + .build(); + } + + public static Types.Address generateAddress() { + return Types.Address.newBuilder() + .setObjectId(generateObjectID()) + .setContainerId(generateContainerID()) + .build(); + } + + public static Types.ObjectID generateObjectID() { + return Types.ObjectID.newBuilder() + .setValue(ByteString.copyFrom(new byte[]{1, 2, 3})) + .build(); + } + + public static Iterable generateObjectIDs() { + return Arrays.asList(generateObjectID(), generateObjectID()); + } + + public static Types.ContainerID generateContainerID() { + return Types.ContainerID.newBuilder() + .setValue(ByteString.copyFrom(new byte[]{1, 2, 3})) + .build(); + } + + public static Iterable generateContainerIDs() { + return Arrays.asList(generateContainerID(), generateContainerID()); + } + + public static Types.Signature generateSignature() { + return Types.Signature.newBuilder() + .setKey(ByteString.copyFrom(new byte[]{1})) + .setSign(ByteString.copyFrom(new byte[]{2})) + .setScheme(Types.SignatureScheme.forNumber(new Random().nextInt(3))) + .build(); + } + + public static Types.Checksum generateChecksum() { + return Types.Checksum.newBuilder() + .setType(Types.ChecksumType.SHA256) + .setSum(ByteString.copyFrom(new byte[]{1, 2, 3})) + .build(); + } +} diff --git a/client/src/test/java/info/frostfs/sdk/testgenerator/SessionGenerator.java b/client/src/test/java/info/frostfs/sdk/testgenerator/SessionGenerator.java new file mode 100644 index 0000000..baa1825 --- /dev/null +++ b/client/src/test/java/info/frostfs/sdk/testgenerator/SessionGenerator.java @@ -0,0 +1,178 @@ +package info.frostfs.sdk.testgenerator; + +import com.google.protobuf.ByteString; +import frostfs.session.Service; +import frostfs.session.Types; + +import java.util.Arrays; +import java.util.Random; + +public class SessionGenerator { + + public static Service.CreateRequest.Body generateCreateRequestBody() { + return Service.CreateRequest.Body.newBuilder() + .setExpiration(555) + .setOwnerId(RefsGenerator.generateOwnerID()) + .build(); + } + + public static Service.CreateRequest generateCreateRequest() { + return Service.CreateRequest.newBuilder() + .setBody(generateCreateRequestBody()) + .setMetaHeader(generateRequestMetaHeader()) + .setVerifyHeader(generateRequestVerificationHeader()) + .build(); + } + + public static Service.CreateResponse.Body generateCreateResponseBody() { + return Service.CreateResponse.Body.newBuilder() + .setId(ByteString.copyFrom(new byte[]{1, 2, 3})) + .setSessionKey(ByteString.copyFrom(new byte[]{4, 5, 6})) + .build(); + } + + public static Service.CreateResponse generateCreateResponse() { + return Service.CreateResponse.newBuilder() + .setBody(generateCreateResponseBody()) + .setMetaHeader(generateResponseMetaHeader()) + .setVerifyHeader(generateResponseVerificationHeader()) + .build(); + } + + public static Types.ResponseVerificationHeader generateResponseVerificationHeader() { + return generateResponseVerificationHeader(true); + } + + public static Types.ResponseVerificationHeader generateResponseVerificationHeader(boolean withOrigin) { + var builder = Types.ResponseVerificationHeader.newBuilder() + .setBodySignature(RefsGenerator.generateSignature()) + .setMetaSignature(RefsGenerator.generateSignature()) + .setOriginSignature(RefsGenerator.generateSignature()); + + if (withOrigin) { + builder.setOrigin(generateResponseVerificationHeader(false)); + } + + return builder.build(); + } + + public static Types.ResponseMetaHeader generateResponseMetaHeader() { + return generateResponseMetaHeader(true); + } + + public static Types.ResponseMetaHeader generateResponseMetaHeader(boolean withOrigin) { + var builder = Types.ResponseMetaHeader.newBuilder() + .setEpoch(13) + .setTtl(100) + .addAllXHeaders(generateXHeaders()) + .setVersion(RefsGenerator.generateVersion()) + .setStatus(StatusGenerator.generateStatus()); + + if (withOrigin) { + builder.setOrigin(generateResponseMetaHeader(false)); + } + + return builder.build(); + } + + public static Types.RequestVerificationHeader generateRequestVerificationHeader() { + return generateRequestVerificationHeader(true); + } + + public static Types.RequestVerificationHeader generateRequestVerificationHeader(boolean withOrigin) { + var builder = Types.RequestVerificationHeader.newBuilder() + .setBodySignature(RefsGenerator.generateSignature()) + .setMetaSignature(RefsGenerator.generateSignature()) + .setOriginSignature(RefsGenerator.generateSignature()); + + if (withOrigin) { + builder.setOrigin(generateRequestVerificationHeader(false)); + } + + return builder.build(); + } + + public static Types.RequestMetaHeader generateRequestMetaHeader() { + return generateRequestMetaHeader(true); + } + + public static Types.RequestMetaHeader generateRequestMetaHeader(boolean withOrigin) { + var builder = Types.RequestMetaHeader.newBuilder() + .setEpoch(13) + .setTtl(100) + .setMagicNumber(1337) + .addAllXHeaders(generateXHeaders()) + .setVersion(RefsGenerator.generateVersion()) + .setSessionToken(generateSessionToken()); + + if (withOrigin) { + builder.setOrigin(generateRequestMetaHeader(false)); + } + + return builder.build(); + } + + public static Types.SessionToken.Body.TokenLifetime generateTokenLifeTime() { + return Types.SessionToken.Body.TokenLifetime.newBuilder() + .setExp(1) + .setIat(2) + .setNbf(3) + .build(); + } + + public static Types.SessionToken.Body generateSessionTokenBody() { + var builder = Types.SessionToken.Body.newBuilder() + .setId(ByteString.copyFrom(new byte[]{1})) + .setSessionKey(ByteString.copyFrom(new byte[]{2})) + .setOwnerId(RefsGenerator.generateOwnerID()) + .setLifetime(generateTokenLifeTime()); + + if (new Random().nextBoolean()) { + builder.setContainer(generateContainerSessionContext()); + } else { + builder.setObject(generateObjectSessionContext()); + } + + return builder.build(); + } + + public static Types.SessionToken generateSessionToken() { + return Types.SessionToken.newBuilder() + .setBody(generateSessionTokenBody()) + .setSignature(RefsGenerator.generateSignature()) + .build(); + } + + public static Types.ObjectSessionContext.Target generateObjectSessionContextTarget() { + return Types.ObjectSessionContext.Target.newBuilder() + .setContainer(RefsGenerator.generateContainerID()) + .addObjects(RefsGenerator.generateObjectID()) + .build(); + } + + public static Types.ObjectSessionContext generateObjectSessionContext() { + return Types.ObjectSessionContext.newBuilder() + .setVerb(Types.ObjectSessionContext.Verb.HEAD) + .setTarget(generateObjectSessionContextTarget()) + .build(); + } + + public static Types.ContainerSessionContext generateContainerSessionContext() { + return Types.ContainerSessionContext.newBuilder() + .setVerb(Types.ContainerSessionContext.Verb.DELETE) + .setWildcard(true) + .setContainerId(RefsGenerator.generateContainerID()) + .build(); + } + + public static Types.XHeader generateXHeader() { + return Types.XHeader.newBuilder() + .setKey("key") + .setValue("val") + .build(); + } + + public static Iterable generateXHeaders() { + return Arrays.asList(generateXHeader(), generateXHeader()); + } +} diff --git a/client/src/test/java/info/frostfs/sdk/testgenerator/StatusGenerator.java b/client/src/test/java/info/frostfs/sdk/testgenerator/StatusGenerator.java new file mode 100644 index 0000000..0a8fb83 --- /dev/null +++ b/client/src/test/java/info/frostfs/sdk/testgenerator/StatusGenerator.java @@ -0,0 +1,29 @@ +package info.frostfs.sdk.testgenerator; + +import com.google.protobuf.ByteString; +import frostfs.status.Types; + +import java.util.Arrays; + +public class StatusGenerator { + + public static Types.Status.Detail generateDetail() { + return Types.Status.Detail.newBuilder() + .setId(345) + .setValue(ByteString.copyFrom("value".getBytes())) + .build(); + } + + public static Iterable generateDetails() { + return Arrays.asList(generateDetail(), generateDetail()); + } + + public static Types.Status generateStatus() { + return Types.Status.newBuilder() + .setCode(2) + .setMessage("some string") + .addAllDetails(generateDetails()) + .build(); + } + +} diff --git a/client/src/test/resources/test_chain_raw.json b/client/src/test/resources/test_chain_raw.json new file mode 100644 index 0000000..60dfc3b --- /dev/null +++ b/client/src/test/resources/test_chain_raw.json @@ -0,0 +1,30 @@ +{ + "ID": "", + "Rules": [ + { + "Status": "Allow", + "Actions": { + "Inverted": false, + "Names": [ + "GetObject" + ] + }, + "Resources": { + "Inverted": false, + "Names": [ + "native:object/*" + ] + }, + "Any": false, + "Condition": [ + { + "Op": "StringEquals", + "Object": "Resource", + "Key": "Department", + "Value": "HR" + } + ] + } + ], + "MatchType": "DenyPriority" +} \ No newline at end of file diff --git a/cryptography/src/main/java/info/frostfs/sdk/Helper.java b/cryptography/src/main/java/info/frostfs/sdk/Helper.java index 29e7fdc..7ee18d2 100644 --- a/cryptography/src/main/java/info/frostfs/sdk/Helper.java +++ b/cryptography/src/main/java/info/frostfs/sdk/Helper.java @@ -3,6 +3,7 @@ package info.frostfs.sdk; import com.google.protobuf.ByteString; import com.google.protobuf.Message; import info.frostfs.sdk.exceptions.ValidationFrostFSException; +import org.apache.commons.lang3.StringUtils; import org.bouncycastle.crypto.digests.RIPEMD160Digest; import java.math.BigInteger; @@ -15,6 +16,7 @@ import static java.util.Objects.isNull; public class Helper { private static final String SHA256 = "SHA-256"; private static final int RIPEMD_160_HASH_BYTE_LENGTH = 20; + private static final int HEX_RADIX = 16; private Helper() { } @@ -62,4 +64,12 @@ public class Helper { return String.format("%0" + (value.length << 1) + "x", new BigInteger(1, value)); } + + public static byte[] getByteArrayFromHex(String hex) { + if (StringUtils.isBlank(hex)) { + throw new ValidationFrostFSException(INPUT_PARAM_IS_MISSING); + } + + return new BigInteger(hex, HEX_RADIX).toByteArray(); + } } diff --git a/cryptography/src/test/java/info/frostfs/sdk/HelperTest.java b/cryptography/src/test/java/info/frostfs/sdk/HelperTest.java index cb1e854..536e881 100644 --- a/cryptography/src/test/java/info/frostfs/sdk/HelperTest.java +++ b/cryptography/src/test/java/info/frostfs/sdk/HelperTest.java @@ -108,4 +108,23 @@ public class HelperTest { assertThrows(ValidationFrostFSException.class, () -> Helper.getHexString(null)); assertThrows(ValidationFrostFSException.class, () -> Helper.getHexString(new byte[]{})); } + + @Test + void getByteArrayFromHex_success() { + //Given + var value = "0102030405"; + + //When + var result = Helper.getByteArrayFromHex(value); + + //Then + assertThat(result).containsOnly(new byte[]{1, 2, 3, 4, 5}); + } + + @Test + void getByteArrayFromHex_wrong() { + //When + Then + assertThrows(ValidationFrostFSException.class, () -> Helper.getByteArrayFromHex(null)); + assertThrows(ValidationFrostFSException.class, () -> Helper.getByteArrayFromHex("")); + } } diff --git a/models/src/main/java/info/frostfs/sdk/dto/chain/Chain.java b/models/src/main/java/info/frostfs/sdk/dto/chain/Chain.java new file mode 100644 index 0000000..984b163 --- /dev/null +++ b/models/src/main/java/info/frostfs/sdk/dto/chain/Chain.java @@ -0,0 +1,10 @@ +package info.frostfs.sdk.dto.chain; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class Chain { + private final byte[] raw; +} diff --git a/models/src/main/java/info/frostfs/sdk/dto/chain/ChainTarget.java b/models/src/main/java/info/frostfs/sdk/dto/chain/ChainTarget.java new file mode 100644 index 0000000..c03a179 --- /dev/null +++ b/models/src/main/java/info/frostfs/sdk/dto/chain/ChainTarget.java @@ -0,0 +1,17 @@ +package info.frostfs.sdk.dto.chain; + +import info.frostfs.sdk.enums.TargetType; +import lombok.EqualsAndHashCode; +import lombok.Getter; + +@Getter +@EqualsAndHashCode +public class ChainTarget { + private final TargetType type; + private final String name; + + public ChainTarget(String name, TargetType type) { + this.name = name; + this.type = type; + } +} diff --git a/models/src/main/java/info/frostfs/sdk/enums/TargetType.java b/models/src/main/java/info/frostfs/sdk/enums/TargetType.java new file mode 100644 index 0000000..d9abde0 --- /dev/null +++ b/models/src/main/java/info/frostfs/sdk/enums/TargetType.java @@ -0,0 +1,34 @@ +package info.frostfs.sdk.enums; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public enum TargetType { + UNDEFINED(0), + NAMESPACE(1), + CONTAINER(2), + USER(3), + GROUP(4), + ; + + private static final Map ENUM_MAP_BY_VALUE; + + static { + Map map = new HashMap<>(); + for (TargetType targetType : TargetType.values()) { + map.put(targetType.value, targetType); + } + ENUM_MAP_BY_VALUE = Collections.unmodifiableMap(map); + } + + public final int value; + + TargetType(int value) { + this.value = value; + } + + public static TargetType get(int value) { + return ENUM_MAP_BY_VALUE.get(value); + } +} diff --git a/models/src/main/java/info/frostfs/sdk/mappers/chain/ChainMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/chain/ChainMapper.java new file mode 100644 index 0000000..20197f7 --- /dev/null +++ b/models/src/main/java/info/frostfs/sdk/mappers/chain/ChainMapper.java @@ -0,0 +1,31 @@ +package info.frostfs.sdk.mappers.chain; + +import frostfs.ape.Types; +import info.frostfs.sdk.dto.chain.Chain; +import org.apache.commons.collections4.CollectionUtils; + +import java.util.List; +import java.util.stream.Collectors; + +import static java.util.Objects.isNull; + +public class ChainMapper { + private ChainMapper() { + } + + public static List toModels(List chains) { + if (CollectionUtils.isEmpty(chains)) { + return null; + } + + return chains.stream().map(ChainMapper::toModel).collect(Collectors.toList()); + } + + public static Chain toModel(Types.Chain chain) { + if (isNull(chain) || chain.getSerializedSize() == 0) { + return null; + } + + return new Chain(chain.getRaw().toByteArray()); + } +} diff --git a/models/src/main/java/info/frostfs/sdk/mappers/chain/ChainTargetMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/chain/ChainTargetMapper.java new file mode 100644 index 0000000..0751060 --- /dev/null +++ b/models/src/main/java/info/frostfs/sdk/mappers/chain/ChainTargetMapper.java @@ -0,0 +1,33 @@ +package info.frostfs.sdk.mappers.chain; + +import frostfs.ape.Types; +import info.frostfs.sdk.dto.chain.ChainTarget; +import info.frostfs.sdk.exceptions.ProcessFrostFSException; + +import static info.frostfs.sdk.constants.ErrorConst.UNKNOWN_ENUM_VALUE_TEMPLATE; +import static java.util.Objects.isNull; + +public class ChainTargetMapper { + private ChainTargetMapper() { + } + + public static Types.ChainTarget toGrpcMessage(ChainTarget chainTarget) { + if (isNull(chainTarget)) { + return null; + } + + var targetType = Types.TargetType.forNumber(chainTarget.getType().value); + if (isNull(targetType)) { + throw new ProcessFrostFSException(String.format( + UNKNOWN_ENUM_VALUE_TEMPLATE, + Types.ChainTarget.class.getName(), + chainTarget.getType().name() + )); + } + + return Types.ChainTarget.newBuilder() + .setType(targetType) + .setName(chainTarget.getName()) + .build(); + } +} diff --git a/models/src/test/java/info/frostfs/sdk/mappers/chain/ChainMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/chain/ChainMapperTest.java new file mode 100644 index 0000000..7d2c4b6 --- /dev/null +++ b/models/src/test/java/info/frostfs/sdk/mappers/chain/ChainMapperTest.java @@ -0,0 +1,64 @@ +package info.frostfs.sdk.mappers.chain; + +import com.google.protobuf.ByteString; +import frostfs.ape.Types; +import org.junit.jupiter.api.Test; + +import java.util.Collections; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class ChainMapperTest { + + @Test + void toModels_success() { + //Given + var chain1 = Types.Chain.newBuilder() + .setRaw(ByteString.copyFrom(new byte[]{1, 2, 3, 4, 5})) + .build(); + var chain2 = Types.Chain.newBuilder() + .setRaw(ByteString.copyFrom(new byte[]{6, 7, 8, 9, 10})) + .build(); + + //When + var result = ChainMapper.toModels(List.of(chain1, chain2)); + + //Then + assertNotNull(result); + assertThat(result).hasSize(2); + assertThat(result.get(0).getRaw()).containsOnly(chain1.getRaw().toByteArray()); + assertThat(result.get(1).getRaw()).containsOnly(chain2.getRaw().toByteArray()); + } + + @Test + void toModels_null() { + //When + Then + assertNull(ChainMapper.toModels(null)); + assertNull(ChainMapper.toModels(Collections.emptyList())); + } + + @Test + void toModel_success() { + //Given + var chain = Types.Chain.newBuilder() + .setRaw(ByteString.copyFrom(new byte[]{1, 2, 3, 4, 5})) + .build(); + + //When + var result = ChainMapper.toModel(chain); + + //Then + assertNotNull(result); + assertThat(result.getRaw()).containsOnly(chain.getRaw().toByteArray()); + } + + @Test + void toModel_null() { + //When + Then + assertNull(ChainMapper.toModel(null)); + assertNull(ChainMapper.toModel(Types.Chain.getDefaultInstance())); + } +} diff --git a/models/src/test/java/info/frostfs/sdk/mappers/chain/ChainTargetMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/chain/ChainTargetMapperTest.java new file mode 100644 index 0000000..b23b3b1 --- /dev/null +++ b/models/src/test/java/info/frostfs/sdk/mappers/chain/ChainTargetMapperTest.java @@ -0,0 +1,52 @@ +package info.frostfs.sdk.mappers.chain; + +import frostfs.ape.Types; +import info.frostfs.sdk.dto.chain.ChainTarget; +import info.frostfs.sdk.enums.TargetType; +import info.frostfs.sdk.exceptions.ProcessFrostFSException; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.mockito.MockedStatic; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mockStatic; + +public class ChainTargetMapperTest { + + @ParameterizedTest + @EnumSource(value = TargetType.class) + void toGrpcMessage_success(TargetType targetType) { + //Given + var target = new ChainTarget("BzQw5HH3feoxFDD5tCT87Y1726qzgLfxEE7wgtoRzB3R", targetType); + + + //When + var result = ChainTargetMapper.toGrpcMessage(target); + + //Then + assertNotNull(result); + assertEquals(target.getName(), result.getName()); + assertEquals(target.getType().value, result.getTypeValue()); + } + + @Test + void toGrpcMessage_null() { + //When + Then + assertNull(ChainTargetMapper.toGrpcMessage(null)); + } + + @Test + void toGrpcMessage_notValidScheme() { + //Given + var target = new ChainTarget("BzQw5HH3feoxFDD5tCT87Y1726qzgLfxEE7wgtoRzB3R", TargetType.UNDEFINED); + + //When + Then + try (MockedStatic mockStatic = mockStatic(Types.TargetType.class)) { + mockStatic.when(() -> Types.TargetType.forNumber(target.getType().value)) + .thenReturn(null); + + assertThrows(ProcessFrostFSException.class, () -> ChainTargetMapper.toGrpcMessage(target)); + } + } +} diff --git a/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectFilterMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectFilterMapperTest.java index 9fbc109..268bf43 100644 --- a/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectFilterMapperTest.java +++ b/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectFilterMapperTest.java @@ -39,7 +39,7 @@ public class ObjectFilterMapperTest { } @Test - void toGrpcMessage_notValidScheme() { + void toGrpcMessage_notValidType() { //Given var objectFilter = new ObjectFilter.FilterByAttribute(UNSPECIFIED, "key", "value"); diff --git a/pom.xml b/pom.xml index b8d61db..bd46c8b 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ 11 UTF-8 checkstyle.xml - 5.12.0 + 5.14.2 5.10.3 3.26.3 1.18.34 @@ -64,6 +64,12 @@ ${mockito.version} test + + org.mockito + mockito-junit-jupiter + ${mockito.version} + test + diff --git a/protos/src/main/proto/accounting/service.proto b/protos/src/main/proto/accounting/service.proto index 414d71d..39e838a 100644 --- a/protos/src/main/proto/accounting/service.proto +++ b/protos/src/main/proto/accounting/service.proto @@ -2,20 +2,19 @@ syntax = "proto3"; package neo.fs.v2.accounting; -option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/accounting/grpc;accounting"; option java_package = "frostfs.accounting"; import "accounting/types.proto"; import "refs/types.proto"; import "session/types.proto"; -// Accounting service provides methods for interaction with NeoFS sidechain via -// other NeoFS nodes to get information about the account balance. Deposit and -// Withdraw operations can't be implemented here, as they require Mainnet NeoFS -// smart contract invocation. Transfer operations between internal NeoFS -// accounts are possible if both use the same token type. +// Accounting service provides methods for interaction with FrostFS sidechain +// via other FrostFS nodes to get information about the account balance. Deposit +// and Withdraw operations can't be implemented here, as they require Mainnet +// FrostFS smart contract invocation. Transfer operations between internal +// FrostFS accounts are possible if both use the same token type. service AccountingService { - // Returns the amount of funds in GAS token for the requested NeoFS account. + // Returns the amount of funds in GAS token for the requested FrostFS account. // // Statuses: // - **OK** (0, SECTION_SUCCESS): @@ -27,9 +26,9 @@ service AccountingService { // BalanceRequest message message BalanceRequest { // To indicate the account for which the balance is requested, its identifier - // is used. It can be any existing account in NeoFS sidechain `Balance` smart - // contract. If omitted, client implementation MUST set it to the request's - // signer `OwnerID`. + // is used. It can be any existing account in FrostFS sidechain `Balance` + // smart contract. If omitted, client implementation MUST set it to the + // request's signer `OwnerID`. message Body { // Valid user identifier in `OwnerID` format for which the balance is // requested. Required field. diff --git a/protos/src/main/proto/accounting/types.proto b/protos/src/main/proto/accounting/types.proto index 61952f5..db85a59 100644 --- a/protos/src/main/proto/accounting/types.proto +++ b/protos/src/main/proto/accounting/types.proto @@ -2,10 +2,9 @@ syntax = "proto3"; package neo.fs.v2.accounting; -option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/accounting/grpc;accounting"; option java_package = "frostfs.accounting"; -// Standard floating point data type can't be used in NeoFS due to inexactness +// Standard floating point data type can't be used in FrostFS due to inexactness // of the result when doing lots of small number operations. To solve the lost // precision issue, special `Decimal` format is used for monetary computations. // diff --git a/protos/src/main/proto/acl/types.proto b/protos/src/main/proto/acl/types.proto index b59ac7d..981e946 100644 --- a/protos/src/main/proto/acl/types.proto +++ b/protos/src/main/proto/acl/types.proto @@ -2,10 +2,10 @@ syntax = "proto3"; package neo.fs.v2.acl; -option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl/grpc;acl"; option java_package = "frostfs.acl"; import "refs/types.proto"; +import "ape/types.proto"; // Target role of the access control rule in access control list. enum Role { @@ -88,14 +88,14 @@ enum HeaderType { // Filter object headers OBJECT = 2; - // Filter service headers. These are not processed by NeoFS nodes and + // Filter service headers. These are not processed by FrostFS nodes and // exist for service use only. SERVICE = 3; } // Describes a single eACL rule. message EACLRecord { - // NeoFS request Verb to match + // FrostFS request Verb to match Operation operation = 1 [ json_name = "operation" ]; // Rule execution result. Either allows or denies access if filters match. @@ -164,7 +164,7 @@ message EACLRecord { // Extended ACL rules table. A list of ACL rules defined additionally to Basic // ACL. Extended ACL rules can be attached to a container and can be updated // or may be defined in `BearerToken` structure. Please see the corresponding -// NeoFS Technical Specification section for detailed description. +// FrostFS Technical Specification section for detailed description. message EACLTable { // eACL format version. Effectively, the version of API library used to create // eACL Table. @@ -194,6 +194,9 @@ message BearerToken { // container. If it contains `container_id` field, bearer token is only // valid for this specific container. Otherwise, any container of the same // owner is allowed. + // + // Deprecated: eACL tables are no longer relevant - `APEOverrides` should be + // used instead. EACLTable eacl_table = 1 [ json_name = "eaclTable" ]; // `OwnerID` defines to whom the token was issued. It must match the request @@ -218,6 +221,24 @@ message BearerToken { // AllowImpersonate flag to consider token signer as request owner. // If this field is true extended ACL table in token body isn't processed. bool allow_impersonate = 4 [ json_name = "allowImpersonate" ]; + + // APEOverride is the list of APE chains defined for a target. + // These chains are meant to serve as overrides to the already defined (or + // even undefined) APE chains for the target (see contract `Policy`). + // + // The server-side processing of the bearer token with set APE overrides + // must verify if a client is permitted to override chains for the target, + // preventing unauthorized access through the APE mechanism. + message APEOverride { + // Target for which chains are applied. + frostfs.v2.ape.ChainTarget target = 1 [ json_name = "target" ]; + + // The list of APE chains. + repeated frostfs.v2.ape.Chain chains = 2 [ json_name = "chains" ]; + } + + // APE override for the target. + APEOverride ape_override = 5 [ json_name = "apeOverride" ]; } // Bearer Token body Body body = 1 [ json_name = "body" ]; diff --git a/protos/src/main/proto/apemanager/types.proto b/protos/src/main/proto/ape/types.proto similarity index 73% rename from protos/src/main/proto/apemanager/types.proto rename to protos/src/main/proto/ape/types.proto index 7ca80ae..96578bc 100644 --- a/protos/src/main/proto/apemanager/types.proto +++ b/protos/src/main/proto/ape/types.proto @@ -1,9 +1,8 @@ syntax = "proto3"; -package frostfs.v2.apemanager; +package frostfs.v2.ape; -option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/apemanager/grpc;apemanager"; -option java_package = "frostfs.apemanager"; +option java_package = "frostfs.ape"; // TargetType is a type target to which a rule chain is defined. enum TargetType { diff --git a/protos/src/main/proto/apemanager/service.proto b/protos/src/main/proto/apemanager/service.proto index d4eeca2..f75f013 100644 --- a/protos/src/main/proto/apemanager/service.proto +++ b/protos/src/main/proto/apemanager/service.proto @@ -2,12 +2,11 @@ syntax = "proto3"; package frostfs.v2.apemanager; -import "apemanager/types.proto"; -import "session/types.proto"; - -option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/apemanager/grpc;apemanager"; option java_package = "frostfs.apemanager"; +import "ape/types.proto"; +import "session/types.proto"; + // `APEManagerService` provides API to manage rule chains within sidechain's // `Policy` smart contract. service APEManagerService { @@ -53,10 +52,10 @@ service APEManagerService { message AddChainRequest { message Body { // A target for which a rule chain is added. - ChainTarget target = 1; + frostfs.v2.ape.ChainTarget target = 1; // The chain to set for the target. - Chain chain = 2; + frostfs.v2.ape.Chain chain = 2; } // The request's body. @@ -96,7 +95,7 @@ message AddChainResponse { message RemoveChainRequest { message Body { // Target for which a rule chain is removed. - ChainTarget target = 1; + frostfs.v2.ape.ChainTarget target = 1; // Chain ID assigned for the rule chain. bytes chain_id = 2; @@ -136,7 +135,7 @@ message RemoveChainResponse { message ListChainsRequest { message Body { // Target for which rule chains are listed. - ChainTarget target = 1; + frostfs.v2.ape.ChainTarget target = 1; } // The request's body. @@ -155,7 +154,7 @@ message ListChainsRequest { message ListChainsResponse { message Body { // The list of chains defined for the reqeusted target. - repeated Chain chains = 1; + repeated frostfs.v2.ape.Chain chains = 1; } // The response's body. @@ -169,4 +168,4 @@ message ListChainsResponse { // authenticate the nodes of the message route and check the correctness of // transmission. neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; -} \ No newline at end of file +} diff --git a/protos/src/main/proto/container/service.proto b/protos/src/main/proto/container/service.proto index 6a85979..a650502 100644 --- a/protos/src/main/proto/container/service.proto +++ b/protos/src/main/proto/container/service.proto @@ -2,17 +2,15 @@ syntax = "proto3"; package neo.fs.v2.container; -option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container/grpc;container"; option java_package = "frostfs.container"; -import "acl/types.proto"; import "container/types.proto"; import "refs/types.proto"; import "session/types.proto"; // `ContainerService` provides API to interact with `Container` smart contract -// in NeoFS sidechain via other NeoFS nodes. All of those actions can be done -// equivalently by directly issuing transactions and RPC calls to sidechain +// in FrostFS sidechain via other FrostFS nodes. All of those actions can be +// done equivalently by directly issuing transactions and RPC calls to sidechain // nodes. service ContainerService { // `Put` invokes `Container` smart contract's `Put` method and returns @@ -25,7 +23,7 @@ service ContainerService { // request to save the container has been sent to the sidechain; // - Common failures (SECTION_FAILURE_COMMON); // - **CONTAINER_ACCESS_DENIED** (3074, SECTION_CONTAINER): \ - // container create access denied. + // container create access denied. rpc Put(PutRequest) returns (PutResponse); // `Delete` invokes `Container` smart contract's `Delete` method and returns @@ -38,7 +36,7 @@ service ContainerService { // request to remove the container has been sent to the sidechain; // - Common failures (SECTION_FAILURE_COMMON); // - **CONTAINER_ACCESS_DENIED** (3074, SECTION_CONTAINER): \ - // container delete access denied. + // container delete access denied. rpc Delete(DeleteRequest) returns (DeleteResponse); // Returns container structure from `Container` smart contract storage. @@ -50,7 +48,7 @@ service ContainerService { // - **CONTAINER_NOT_FOUND** (3072, SECTION_CONTAINER): \ // requested container not found; // - **CONTAINER_ACCESS_DENIED** (3074, SECTION_CONTAINER): \ - // access to container is denied. + // access to container is denied. rpc Get(GetRequest) returns (GetResponse); // Returns all owner's containers from 'Container` smart contract' storage. @@ -60,47 +58,11 @@ service ContainerService { // container list has been successfully read; // - Common failures (SECTION_FAILURE_COMMON); // - **CONTAINER_ACCESS_DENIED** (3074, SECTION_CONTAINER): \ - // container list access denied. + // container list access denied. rpc List(ListRequest) returns (ListResponse); - - // Invokes 'SetEACL' method of 'Container` smart contract and returns response - // immediately. After one more block in sidechain, changes in an Extended ACL - // are added into smart contract storage. - // - // Statuses: - // - **OK** (0, SECTION_SUCCESS): \ - // request to save container eACL has been sent to the sidechain; - // - Common failures (SECTION_FAILURE_COMMON); - // - **CONTAINER_ACCESS_DENIED** (3074, SECTION_CONTAINER): \ - // set container eACL access denied. - rpc SetExtendedACL(SetExtendedACLRequest) returns (SetExtendedACLResponse); - - // Returns Extended ACL table and signature from `Container` smart contract - // storage. - // - // Statuses: - // - **OK** (0, SECTION_SUCCESS): \ - // container eACL has been successfully read; - // - Common failures (SECTION_FAILURE_COMMON); - // - **CONTAINER_NOT_FOUND** (3072, SECTION_CONTAINER): \ - // container not found; - // - **EACL_NOT_FOUND** (3073, SECTION_CONTAINER): \ - // eACL table not found; - // - **CONTAINER_ACCESS_DENIED** (3074, SECTION_CONTAINER): \ - // access to container eACL is denied. - rpc GetExtendedACL(GetExtendedACLRequest) returns (GetExtendedACLResponse); - - // Announces the space values used by the container for P2P synchronization. - // - // Statuses: - // - **OK** (0, SECTION_SUCCESS): \ - // estimation of used space has been successfully announced; - // - Common failures (SECTION_FAILURE_COMMON). - rpc AnnounceUsedSpace(AnnounceUsedSpaceRequest) - returns (AnnounceUsedSpaceResponse); } -// New NeoFS Container creation request +// New FrostFS Container creation request message PutRequest { // Container creation request has container structure's signature as a // separate field. It's not stored in sidechain, just verified on container @@ -108,7 +70,7 @@ message PutRequest { // the stable-marshalled container strucutre, hence there is no need for // additional signature checks. message Body { - // Container structure to register in NeoFS + // Container structure to register in FrostFS container.Container container = 1; // Signature of a stable-marshalled container according to RFC-6979. @@ -127,7 +89,7 @@ message PutRequest { neo.fs.v2.session.RequestVerificationHeader verify_header = 3; } -// New NeoFS Container creation response +// New FrostFS Container creation response message PutResponse { // Container put response body contains information about the newly registered // container as seen by `Container` smart contract. `ContainerID` can be @@ -156,7 +118,7 @@ message DeleteRequest { // the container owner's intent. The signature will be verified by `Container` // smart contract, so signing algorithm must be supported by NeoVM. message Body { - // Identifier of the container to delete from NeoFS + // Identifier of the container to delete from FrostFS neo.fs.v2.refs.ContainerID container_id = 1; // `ContainerID` signed with the container owner's key according to @@ -282,150 +244,3 @@ message ListResponse { // transmission. neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; } - -// Set Extended ACL -message SetExtendedACLRequest { - // Set Extended ACL request body does not have separate `ContainerID` - // reference. It will be taken from `EACLTable.container_id` field. - message Body { - // Extended ACL table to set for the container - neo.fs.v2.acl.EACLTable eacl = 1; - - // Signature of stable-marshalled Extended ACL table according to RFC-6979. - neo.fs.v2.refs.SignatureRFC6979 signature = 2; - } - // Body of set extended acl request message. - Body body = 1; - - // Carries request meta information. Header data is used only to regulate - // message transport and does not affect request execution. - neo.fs.v2.session.RequestMetaHeader meta_header = 2; - - // Carries request verification information. This header is used to - // authenticate the nodes of the message route and check the correctness of - // transmission. - neo.fs.v2.session.RequestVerificationHeader verify_header = 3; -} - -// Set Extended ACL -message SetExtendedACLResponse { - // `SetExtendedACLResponse` has an empty body because the operation is - // asynchronous and the update should be reflected in `Container` smart - // contract's storage after next block is issued in sidechain. - message Body {} - - // Body of set extended acl response message. - Body body = 1; - - // Carries response meta information. Header data is used only to regulate - // message transport and does not affect request execution. - neo.fs.v2.session.ResponseMetaHeader meta_header = 2; - - // Carries response verification information. This header is used to - // authenticate the nodes of the message route and check the correctness of - // transmission. - neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; -} - -// Get Extended ACL -message GetExtendedACLRequest { - // Get Extended ACL request body - message Body { - // Identifier of the container having Extended ACL - neo.fs.v2.refs.ContainerID container_id = 1; - } - - // Body of get extended acl request message. - Body body = 1; - - // Carries request meta information. Header data is used only to regulate - // message transport and does not affect request execution. - neo.fs.v2.session.RequestMetaHeader meta_header = 2; - - // Carries request verification information. This header is used to - // authenticate the nodes of the message route and check the correctness of - // transmission. - neo.fs.v2.session.RequestVerificationHeader verify_header = 3; -} - -// Get Extended ACL -message GetExtendedACLResponse { - // Get Extended ACL Response body can be empty if the requested container does - // not have Extended ACL Table attached or Extended ACL has not been allowed - // at the time of container creation. - message Body { - // Extended ACL requested, if available - neo.fs.v2.acl.EACLTable eacl = 1; - - // Signature of stable-marshalled Extended ACL according to RFC-6979. - neo.fs.v2.refs.SignatureRFC6979 signature = 2; - - // Session token if Extended ACL was set within a session - neo.fs.v2.session.SessionToken session_token = 3; - } - // Body of get extended acl response message. - Body body = 1; - - // Carries response meta information. Header data is used only to regulate - // message transport and does not affect request execution. - neo.fs.v2.session.ResponseMetaHeader meta_header = 2; - - // Carries response verification information. This header is used to - // authenticate the nodes of the message route and check the correctness of - // transmission. - neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; -} - -// Announce container used space -message AnnounceUsedSpaceRequest { - // Container used space announcement body. - message Body { - // Announcement contains used space information for a single container. - message Announcement { - // Epoch number for which the container size estimation was produced. - uint64 epoch = 1; - - // Identifier of the container. - neo.fs.v2.refs.ContainerID container_id = 2; - - // Used space is a sum of object payload sizes of a specified - // container, stored in the node. It must not include inhumed objects. - uint64 used_space = 3; - } - - // List of announcements. If nodes share several containers, - // announcements are transferred in a batch. - repeated Announcement announcements = 1; - } - - // Body of announce used space request message. - Body body = 1; - - // Carries request meta information. Header data is used only to regulate - // message transport and does not affect request execution. - neo.fs.v2.session.RequestMetaHeader meta_header = 2; - - // Carries request verification information. This header is used to - // authenticate the nodes of the message route and check the correctness of - // transmission. - neo.fs.v2.session.RequestVerificationHeader verify_header = 3; -} - -// Announce container used space -message AnnounceUsedSpaceResponse { - // `AnnounceUsedSpaceResponse` has an empty body because announcements are - // one way communication. - message Body {} - - // Body of announce used space response message. - Body body = 1; - - // Carries response meta information. Header data is used only to regulate - // message transport and does not affect request execution. - neo.fs.v2.session.ResponseMetaHeader meta_header = 2; - - // Carries response verification information. This header is used to - // authenticate the nodes of the message route and check the correctness of - // transmission. - neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; -} diff --git a/protos/src/main/proto/container/types.proto b/protos/src/main/proto/container/types.proto index fc523ca..7fbc8e5 100644 --- a/protos/src/main/proto/container/types.proto +++ b/protos/src/main/proto/container/types.proto @@ -2,7 +2,6 @@ syntax = "proto3"; package neo.fs.v2.container; -option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container/grpc;container"; option java_package = "frostfs.container"; import "netmap/types.proto"; @@ -50,7 +49,7 @@ message Container { // (`__NEOFS__DISABLE_HOMOMORPHIC_HASHING` is deprecated) \ // Disables homomorphic hashing for the container if the value equals "true" // string. Any other values are interpreted as missing attribute. Container - // could be accepted in a NeoFS network only if the global network hashing + // could be accepted in a FrostFS network only if the global network hashing // configuration value corresponds with that attribute's value. After // container inclusion, network setting is ignored. // diff --git a/protos/src/main/proto/lock/types.proto b/protos/src/main/proto/lock/types.proto index e4a8879..0578882 100644 --- a/protos/src/main/proto/lock/types.proto +++ b/protos/src/main/proto/lock/types.proto @@ -2,7 +2,6 @@ syntax = "proto3"; package neo.fs.v2.lock; -option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/lock/grpc;lock"; option java_package = "frostfs.lock"; import "refs/types.proto"; diff --git a/protos/src/main/proto/netmap/service.proto b/protos/src/main/proto/netmap/service.proto index 7e97e09..ef78107 100644 --- a/protos/src/main/proto/netmap/service.proto +++ b/protos/src/main/proto/netmap/service.proto @@ -2,7 +2,6 @@ syntax = "proto3"; package neo.fs.v2.netmap; -option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap/grpc;netmap"; option java_package = "frostfs.netmap"; import "netmap/types.proto"; @@ -12,7 +11,7 @@ import "session/types.proto"; // `NetmapService` provides methods to work with `Network Map` and the // information required to build it. The resulting `Network Map` is stored in // sidechain `Netmap` smart contract, while related information can be obtained -// from other NeoFS nodes. +// from other FrostFS nodes. service NetmapService { // Get NodeInfo structure from the particular node directly. // Node information can be taken from `Netmap` smart contract. In some cases, @@ -27,7 +26,7 @@ service NetmapService { // - Common failures (SECTION_FAILURE_COMMON). rpc LocalNodeInfo(LocalNodeInfoRequest) returns (LocalNodeInfoResponse); - // Read recent information about the NeoFS network. + // Read recent information about the FrostFS network. // // Statuses: // - **OK** (0, SECTION_SUCCESS): @@ -35,7 +34,7 @@ service NetmapService { // - Common failures (SECTION_FAILURE_COMMON). rpc NetworkInfo(NetworkInfoRequest) returns (NetworkInfoResponse); - // Returns network map snapshot of the current NeoFS epoch. + // Returns network map snapshot of the current FrostFS epoch. // // Statuses: // - **OK** (0, SECTION_SUCCESS): @@ -65,7 +64,7 @@ message LocalNodeInfoRequest { message LocalNodeInfoResponse { // Local Node Info, including API Version in use. message Body { - // Latest NeoFS API version in use + // Latest FrostFS API version in use neo.fs.v2.refs.Version version = 1; // NodeInfo structure with recent information from node itself diff --git a/protos/src/main/proto/netmap/types.proto b/protos/src/main/proto/netmap/types.proto index 3c311ba..f861317 100644 --- a/protos/src/main/proto/netmap/types.proto +++ b/protos/src/main/proto/netmap/types.proto @@ -2,7 +2,6 @@ syntax = "proto3"; package neo.fs.v2.netmap; -option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap/grpc;netmap"; option java_package = "frostfs.netmap"; // Operations on filters @@ -36,6 +35,9 @@ enum Operation { // Logical negation NOT = 9; + + // Matches pattern + LIKE = 10; } // Selector modifier shows how the node set will be formed. By default selector @@ -119,7 +121,7 @@ message PlacementPolicy { // bucket repeated Replica replicas = 1 [ json_name = "replicas" ]; - // Container backup factor controls how deep NeoFS will search for nodes + // Container backup factor controls how deep FrostFS will search for nodes // alternatives to include into container's nodes subset uint32 container_backup_factor = 2 [ json_name = "containerBackupFactor" ]; @@ -133,25 +135,25 @@ message PlacementPolicy { bool unique = 5 [ json_name = "unique" ]; } -// NeoFS node description +// FrostFS node description message NodeInfo { - // Public key of the NeoFS node in a binary format + // Public key of the FrostFS node in a binary format bytes public_key = 1 [ json_name = "publicKey" ]; // Ways to connect to a node repeated string addresses = 2 [ json_name = "addresses" ]; - // Administrator-defined Attributes of the NeoFS Storage Node. + // Administrator-defined Attributes of the FrostFS Storage Node. // // `Attribute` is a Key-Value metadata pair. Key name must be a valid UTF-8 // string. Value can't be empty. // // Attributes can be constructed into a chain of attributes: any attribute can // have a parent attribute and a child attribute (except the first and the - // last one). A string representation of the chain of attributes in NeoFS + // last one). A string representation of the chain of attributes in FrostFS // Storage Node configuration uses ":" and "/" symbols, e.g.: // - // `NEOFS_NODE_ATTRIBUTE_1=key1:val1/key2:val2` + // `FrostFS_NODE_ATTRIBUTE_1=key1:val1/key2:val2` // // Therefore the string attribute representation in the Node configuration // must use "\:", "\/" and "\\" escaped symbols if any of them appears in an @@ -198,8 +200,8 @@ message NodeInfo { // [ISO 3166-2](https://en.wikipedia.org/wiki/ISO_3166-2). Calculated // automatically from `UN-LOCODE` attribute. // * Continent \ - // Node's continent name according to the [Seven-Continent model] - // (https://en.wikipedia.org/wiki/Continent#Number). Calculated + // Node's continent name according to the [Seven-Continent + // model](https://en.wikipedia.org/wiki/Continent#Number). Calculated // automatically from `UN-LOCODE` attribute. // * ExternalAddr // Node's preferred way for communications with external clients. @@ -207,7 +209,7 @@ message NodeInfo { // Must contain a comma-separated list of multi-addresses. // // For detailed description of each well-known attribute please see the - // corresponding section in NeoFS Technical Specification. + // corresponding section in FrostFS Technical Specification. message Attribute { // Key of the node attribute string key = 1 [ json_name = "key" ]; @@ -219,13 +221,13 @@ message NodeInfo { // `Country`. repeated string parents = 3 [ json_name = "parents" ]; } - // Carries list of the NeoFS node attributes in a key-value form. Key name + // Carries list of the FrostFS node attributes in a key-value form. Key name // must be a node-unique valid UTF-8 string. Value can't be empty. NodeInfo // structures with duplicated attribute names or attributes with empty values // will be considered invalid. repeated Attribute attributes = 3 [ json_name = "attributes" ]; - // Represents the enumeration of various states of the NeoFS node. + // Represents the enumeration of various states of the FrostFS node. enum State { // Unknown state UNSPECIFIED = 0; @@ -240,7 +242,7 @@ message NodeInfo { MAINTENANCE = 3; } - // Carries state of the NeoFS node + // Carries state of the FrostFS node State state = 4 [ json_name = "state" ]; } @@ -253,7 +255,7 @@ message Netmap { repeated NodeInfo nodes = 2 [ json_name = "nodes" ]; } -// NeoFS network configuration +// FrostFS network configuration message NetworkConfig { // Single configuration parameter. Key MUST be network-unique. // @@ -272,7 +274,7 @@ message NetworkConfig { // Fee paid for container creation by the container owner. // Value: little-endian integer. Default: 0. // - **EpochDuration** \ - // NeoFS epoch duration measured in Sidechain blocks. + // FrostFS epoch duration measured in Sidechain blocks. // Value: little-endian integer. Default: 0. // - **HomomorphicHashingDisabled** \ // Flag of disabling the homomorphic hashing of objects' payload. @@ -284,8 +286,39 @@ message NetworkConfig { // Flag allowing setting the MAINTENANCE state to storage nodes. // Value: true if any byte != 0. Default: false. // - **MaxObjectSize** \ - // Maximum size of physically stored NeoFS object measured in bytes. + // Maximum size of physically stored FrostFS object measured in bytes. // Value: little-endian integer. Default: 0. + // + // This value refers to the maximum size of a **physically** stored object + // in FrostFS. However, from a user's perspective, the **logical** size of a + // stored object can be significantly larger. The relationship between the + // physical and logical object sizes is governed by the following formula + // + // ```math + // \mathrm{Stored\ Object\ Size} \le + // \frac{ + // \left(\mathrm{Max\ Object\ Size}\right)^2 + // }{ + // \mathrm{Object\ ID\ Size} + // } + // ``` + // + // This arises from the fact that a tombstone, also being an object, stores + // the IDs of inhumed objects and cannot be divided into smaller objects, + // thus having an upper limit for its size. + // + // For example, if: + // * Max Object Size Size = 64 MiB; + // * Object ID Size = 32 B; + // + // then: + // ```math + // \mathrm{Stored\ Object\ Size} \le + // \frac{\left(64\ \mathrm{MiB}\right)^2}{32\ \mathrm{B}} = + // \frac{2^{52}}{2^5}\ \mathrm{B} = + // 2^{47}\ \mathrm{B} = + // 128\ \mathrm{TiB} + // ``` // - **WithdrawFee** \ // Fee paid for withdrawal of funds paid by the account owner. // Value: little-endian integer. Default: 0. @@ -306,18 +339,18 @@ message NetworkConfig { repeated Parameter parameters = 1 [ json_name = "parameters" ]; } -// Information about NeoFS network +// Information about FrostFS network message NetworkInfo { - // Number of the current epoch in the NeoFS network + // Number of the current epoch in the FrostFS network uint64 current_epoch = 1 [ json_name = "currentEpoch" ]; - // Magic number of the sidechain of the NeoFS network + // Magic number of the sidechain of the FrostFS network uint64 magic_number = 2 [ json_name = "magicNumber" ]; - // MillisecondsPerBlock network parameter of the sidechain of the NeoFS + // MillisecondsPerBlock network parameter of the sidechain of the FrostFS // network int64 ms_per_block = 3 [ json_name = "msPerBlock" ]; - // NeoFS network configuration + // FrostFS network configuration NetworkConfig network_config = 4 [ json_name = "networkConfig" ]; } diff --git a/protos/src/main/proto/object/service.proto b/protos/src/main/proto/object/service.proto index 7635f8a..d1c816b 100644 --- a/protos/src/main/proto/object/service.proto +++ b/protos/src/main/proto/object/service.proto @@ -2,7 +2,6 @@ syntax = "proto3"; package neo.fs.v2.object; -option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object/grpc;object"; option java_package = "frostfs.object"; import "object/types.proto"; @@ -46,7 +45,7 @@ service ObjectService { // - **CONTAINER_NOT_FOUND** (3072, SECTION_CONTAINER): \ // object container not found; // - **CONTAINER_ACCESS_DENIED** (3074, SECTION_CONTAINER): \ - // access to container is denied; + // access to container is denied; // - **TOKEN_EXPIRED** (4097, SECTION_SESSION): \ // provided session token has expired. rpc Get(GetRequest) returns (stream GetResponse); @@ -115,7 +114,7 @@ service ObjectService { // - **CONTAINER_NOT_FOUND** (3072, SECTION_CONTAINER): \ // object container not found; // - **CONTAINER_ACCESS_DENIED** (3074, SECTION_CONTAINER): \ - // access to container is denied; + // access to container is denied; // - **TOKEN_EXPIRED** (4097, SECTION_SESSION): \ // provided session token has expired. rpc Delete(DeleteRequest) returns (DeleteResponse); @@ -145,13 +144,13 @@ service ObjectService { // - **CONTAINER_NOT_FOUND** (3072, SECTION_CONTAINER): \ // object container not found; // - **CONTAINER_ACCESS_DENIED** (3074, SECTION_CONTAINER): \ - // access to container is denied; + // access to container is denied; // - **TOKEN_EXPIRED** (4097, SECTION_SESSION): \ // provided session token has expired. rpc Head(HeadRequest) returns (HeadResponse); // Search objects in container. Search query allows to match by Object - // Header's filed values. Please see the corresponding NeoFS Technical + // Header's filed values. Please see the corresponding FrostFS Technical // Specification section for more details. // // Extended headers can change `Search` behaviour: @@ -171,7 +170,7 @@ service ObjectService { // - **CONTAINER_NOT_FOUND** (3072, SECTION_CONTAINER): \ // search container not found; // - **CONTAINER_ACCESS_DENIED** (3074, SECTION_CONTAINER): \ - // access to container is denied; + // access to container is denied; // - **TOKEN_EXPIRED** (4097, SECTION_SESSION): \ // provided session token has expired. rpc Search(SearchRequest) returns (stream SearchResponse); @@ -208,7 +207,7 @@ service ObjectService { // - **CONTAINER_NOT_FOUND** (3072, SECTION_CONTAINER): \ // object container not found; // - **CONTAINER_ACCESS_DENIED** (3074, SECTION_CONTAINER): \ - // access to container is denied; + // access to container is denied; // - **TOKEN_EXPIRED** (4097, SECTION_SESSION): \ // provided session token has expired. rpc GetRange(GetRangeRequest) returns (stream GetRangeResponse); @@ -243,7 +242,7 @@ service ObjectService { // - **CONTAINER_NOT_FOUND** (3072, SECTION_CONTAINER): \ // object container not found; // - **CONTAINER_ACCESS_DENIED** (3074, SECTION_CONTAINER): \ - // access to container is denied; + // access to container is denied; // - **TOKEN_EXPIRED** (4097, SECTION_SESSION): \ // provided session token has expired. rpc GetRangeHash(GetRangeHashRequest) returns (GetRangeHashResponse); @@ -275,7 +274,7 @@ service ObjectService { // - **CONTAINER_NOT_FOUND** (3072, SECTION_CONTAINER): \ // object storage container not found; // - **CONTAINER_ACCESS_DENIED** (3074, SECTION_CONTAINER): \ - // access to container is denied; + // access to container is denied; // - **TOKEN_NOT_FOUND** (4096, SECTION_SESSION): \ // (for trusted object preparation) session private key does not exist or // has @@ -283,6 +282,55 @@ service ObjectService { // - **TOKEN_EXPIRED** (4097, SECTION_SESSION): \ // provided session token has expired. rpc PutSingle(PutSingleRequest) returns (PutSingleResponse); + + // Patch the object. Request uses gRPC stream. First message must set + // the address of the object that is going to get patched. If the object's + // attributes are patched, then these attrubutes must be set only within the + // first stream message. + // + // If the patch request is performed by NOT the object's owner but if the + // actor has the permission to perform the patch, then `OwnerID` of the object + // is changed. In this case the object's owner loses the object's ownership + // after the patch request is successfully done. + // + // As objects are content-addressable the patching causes new object ID + // generation for the patched object. This object id is set witihn + // `PatchResponse`. But the object id may remain unchanged in such cases: + // 1. The chunk of the applying patch contains the same value as the object's + // payload within the same range; + // 2. The patch that reverts the changes applied by preceding patch; + // 3. The application of the same patches for the object a few times. + // + // Extended headers can change `Patch` behaviour: + // * [ __SYSTEM__NETMAP_EPOCH \ + // (`__NEOFS__NETMAP_EPOCH` is deprecated) \ + // Will use the requsted version of Network Map for object placement + // calculation. + // + // Please refer to detailed `XHeader` description. + // + // Statuses: + // - **OK** (0, SECTION_SUCCESS): \ + // object has been successfully patched and saved in the container; + // - Common failures (SECTION_FAILURE_COMMON); + // - **ACCESS_DENIED** (2048, SECTION_OBJECT): \ + // write access to the container is denied; + // - **OBJECT_NOT_FOUND** (2049, SECTION_OBJECT): \ + // object not found in container; + // - **OBJECT_ALREADY_REMOVED** (2052, SECTION_OBJECT): \ + // the requested object has been marked as deleted. + // - **OUT_OF_RANGE** (2053, SECTION_OBJECT): \ + // the requested range is out of bounds; + // - **CONTAINER_NOT_FOUND** (3072, SECTION_CONTAINER): \ + // object storage container not found; + // - **CONTAINER_ACCESS_DENIED** (3074, SECTION_CONTAINER): \ + // access to container is denied; + // - **TOKEN_NOT_FOUND** (4096, SECTION_SESSION): \ + // (for trusted object preparation) session private key does not exist or + // has been deleted; + // - **TOKEN_EXPIRED** (4097, SECTION_SESSION): \ + // provided session token has expired. + rpc Patch(stream PatchRequest) returns (PatchResponse); } // GET object request @@ -583,6 +631,9 @@ message SearchRequest { // object_id of parent // * $Object:split.splitID \ // 16 byte UUIDv4 used to identify the split object hierarchy parts + // * $Object:ec.parent \ + // If the object is stored according to EC policy, then ec_parent + // attribute is set to return an id list of all related EC chunks. // // There are some well-known filter aliases to match objects by certain // properties: @@ -813,4 +864,75 @@ message PutSingleResponse { // authenticate the nodes of the message route and check the correctness of // transmission. neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; -} \ No newline at end of file +} + +// Object PATCH request +message PatchRequest { + // PATCH request body + message Body { + // The address of the object that is requested to get patched. + neo.fs.v2.refs.Address address = 1; + + // New attributes for the object. See `replace_attributes` flag usage to + // define how new attributes should be set. + repeated neo.fs.v2.object.Header.Attribute new_attributes = 2; + + // If this flag is set, then the object's attributes will be entirely + // replaced by `new_attributes` list. The empty `new_attributes` list with + // `replace_attributes = true` just resets attributes list for the object. + // + // Default `false` value for this flag means the attributes will be just + // merged. If the incoming `new_attributes` list contains already existing + // key, then it just replaces it while merging the lists. + bool replace_attributes = 3; + + // The patch for the object's payload. + message Patch { + // The range of the source object for which the payload is replaced by the + // patch's chunk. If the range's `length = 0`, then the patch's chunk is + // just appended to the original payload starting from the `offest` + // without any replace. + Range source_range = 1; + + // The chunk that is being appended to or that replaces the original + // payload on the given range. + bytes chunk = 2; + } + + // The patch that is applied for the object. + Patch patch = 4; + } + + // Body for patch request message. + Body body = 1; + + // Carries request meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.RequestMetaHeader meta_header = 2; + + // Carries request verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.RequestVerificationHeader verify_header = 3; +} + +// Object PATCH response +message PatchResponse { + // PATCH response body + message Body { + // The object ID of the saved patched object. + neo.fs.v2.refs.ObjectID object_id = 1; + } + + // Body for patch response message. + Body body = 1; + + // Carries response meta information. Header data is used only to regulate + // message transport and does not affect request execution. + neo.fs.v2.session.ResponseMetaHeader meta_header = 2; + + // Carries response verification information. This header is used to + // authenticate the nodes of the message route and check the correctness of + // transmission. + neo.fs.v2.session.ResponseVerificationHeader verify_header = 3; +} diff --git a/protos/src/main/proto/object/types.proto b/protos/src/main/proto/object/types.proto index 623c2a3..736e49b 100644 --- a/protos/src/main/proto/object/types.proto +++ b/protos/src/main/proto/object/types.proto @@ -2,7 +2,6 @@ syntax = "proto3"; package neo.fs.v2.object; -option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object/grpc;object"; option java_package = "frostfs.object"; import "refs/types.proto"; @@ -155,7 +154,7 @@ message Header { // MIME Content Type of object's payload // // For detailed description of each well-known attribute please see the - // corresponding section in NeoFS Technical Specification. + // corresponding section in FrostFS Technical Specification. message Attribute { // string key to the object attribute string key = 1 [ json_name = "key" ]; @@ -208,6 +207,18 @@ message Header { uint32 header_length = 4 [ json_name = "headerLength" ]; // Chunk of a parent header. bytes header = 5 [ json_name = "header" ]; + // As the origin object is EC-splitted its identifier is known to all + // chunks as parent. But parent itself can be a part of Split (does not + // relate to EC-split). In this case parent_split_id should be set. + bytes parent_split_id = 6 [ json_name = "parentSplitID" ]; + // EC-parent's parent ID. parent_split_parent_id is set if EC-parent, + // itself, is a part of Split and if an object ID of its parent is + // presented. The field allows to determine how EC-chunk is placed in Split + // hierarchy. + neo.fs.v2.refs.ObjectID parent_split_parent_id = 7 + [ json_name = "parentSplitParentID" ]; + // EC parent's attributes. + repeated Attribute parent_attributes = 8 [ json_name = "parentAttributes" ]; } // Erasure code chunk information. EC ec = 12 [ json_name = "ec" ]; @@ -263,4 +274,4 @@ message ECInfo { } // Chunk stored on the node. repeated Chunk chunks = 1; -} \ No newline at end of file +} diff --git a/protos/src/main/proto/refs/types.proto b/protos/src/main/proto/refs/types.proto index 0ed5840..9c74edc 100644 --- a/protos/src/main/proto/refs/types.proto +++ b/protos/src/main/proto/refs/types.proto @@ -2,10 +2,9 @@ syntax = "proto3"; package neo.fs.v2.refs; -option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs/grpc;refs"; option java_package = "frostfs.refs"; -// Objects in NeoFS are addressed by their ContainerID and ObjectID. +// Objects in FrostFS are addressed by their ContainerID and ObjectID. // // String presentation of `Address` is a concatenation of string encoded // `ContainerID` and `ObjectID` delimited by '/' character. @@ -16,8 +15,9 @@ message Address { ObjectID object_id = 2 [ json_name = "objectID" ]; } -// NeoFS Object unique identifier. Objects are immutable and content-addressed. -// It means `ObjectID` will change if the `header` or the `payload` changes. +// FrostFS Object unique identifier. Objects are immutable and +// content-addressed. It means `ObjectID` will change if the `header` or the +// `payload` changes. // // `ObjectID` is a 32 byte long // [SHA256](https://csrc.nist.gov/publications/detail/fips/180/4/final) hash of @@ -37,7 +37,7 @@ message ObjectID { bytes value = 1 [ json_name = "value" ]; } -// NeoFS container identifier. Container structures are immutable and +// FrostFS container identifier. Container structures are immutable and // content-addressed. // // `ContainerID` is a 32 byte long @@ -90,7 +90,7 @@ message Version { uint32 minor = 2 [ json_name = "minor" ]; } -// Signature of something in NeoFS. +// Signature of something in FrostFS. message Signature { // Public key used for signing bytes key = 1 [ json_name = "key" ]; diff --git a/protos/src/main/proto/session/service.proto b/protos/src/main/proto/session/service.proto index b31bd8e..17fd756 100644 --- a/protos/src/main/proto/session/service.proto +++ b/protos/src/main/proto/session/service.proto @@ -2,7 +2,6 @@ syntax = "proto3"; package neo.fs.v2.session; -option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session/grpc;session"; option java_package = "frostfs.session"; import "refs/types.proto"; @@ -11,7 +10,7 @@ import "session/types.proto"; // `SessionService` allows to establish a temporary trust relationship between // two peer nodes and generate a `SessionToken` as the proof of trust to be // attached in requests for further verification. Please see corresponding -// section of NeoFS Technical Specification for details. +// section of FrostFS Technical Specification for details. service SessionService { // Open a new session between two peers. // diff --git a/protos/src/main/proto/session/types.proto b/protos/src/main/proto/session/types.proto index 9f5259a..7e356de 100644 --- a/protos/src/main/proto/session/types.proto +++ b/protos/src/main/proto/session/types.proto @@ -2,7 +2,6 @@ syntax = "proto3"; package neo.fs.v2.session; -option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session/grpc;session"; option java_package = "frostfs.session"; import "refs/types.proto"; @@ -36,6 +35,9 @@ message ObjectSessionContext { // Refers to object.GetRangeHash RPC call RANGEHASH = 7; + + // Refers to object.Patch RPC call + PATCH = 8; } // Type of request for which the token is issued Verb verb = 1 [ json_name = "verb" ]; @@ -47,7 +49,7 @@ message ObjectSessionContext { refs.ContainerID container = 1 [ json_name = "container" ]; // Indicates which objects the session is spread to. Objects are expected - // to be stored in the NeoFS container referenced by `container` field. + // to be stored in the FrostFS container referenced by `container` field. // Each element MUST have correct format. repeated refs.ObjectID objects = 2 [ json_name = "objects" ]; } @@ -85,7 +87,7 @@ message ContainerSessionContext { refs.ContainerID container_id = 3 [ json_name = "containerID" ]; } -// NeoFS Session Token. +// FrostFS Session Token. message SessionToken { // Session Token body message Body { @@ -123,7 +125,7 @@ message SessionToken { } // Session Token contains the proof of trust between peers to be attached in // requests for further verification. Please see corresponding section of - // NeoFS Technical Specification for details. + // FrostFS Technical Specification for details. Body body = 1 [ json_name = "body" ]; // Signature of `SessionToken` information @@ -183,7 +185,7 @@ message RequestMetaHeader { // `RequestMetaHeader` of the origin request RequestMetaHeader origin = 7 [ json_name = "origin" ]; - // NeoFS network magic. Must match the value for the network + // FrostFS network magic. Must match the value for the network // that the server belongs to. uint64 magic_number = 8 [ json_name = "magicNumber" ]; } diff --git a/protos/src/main/proto/status/types.proto b/protos/src/main/proto/status/types.proto index 7d8f8e9..8e61ae5 100644 --- a/protos/src/main/proto/status/types.proto +++ b/protos/src/main/proto/status/types.proto @@ -2,15 +2,14 @@ syntax = "proto3"; package neo.fs.v2.status; -option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/status/grpc;status"; option java_package = "frostfs.status"; -// Declares the general format of the status returns of the NeoFS RPC protocol. -// Status is present in all response messages. Each RPC of NeoFS protocol -// describes the possible outcomes and details of the operation. +// Declares the general format of the status returns of the FrostFS RPC +// protocol. Status is present in all response messages. Each RPC of FrostFS +// protocol describes the possible outcomes and details of the operation. // // Each status is assigned a one-to-one numeric code. Any unique result of an -// operation in NeoFS is unambiguously associated with the code value. +// operation in FrostFS is unambiguously associated with the code value. // // Numerical set of codes is split into 1024-element sections. An enumeration // is defined for each section. Values can be referred to in the following ways: @@ -78,7 +77,7 @@ enum Section { SECTION_APE_MANAGER = 5; } -// Section of NeoFS successful return codes. +// Section of FrostFS successful return codes. enum Success { // [**0**] Default success. Not detailed. // If the server cannot match successful outcome to the code, it should @@ -93,9 +92,9 @@ enum CommonFail { // use this code. INTERNAL = 0; - // [**1025**] Wrong magic of the NeoFS network. + // [**1025**] Wrong magic of the FrostFS network. // Details: - // - [**0**] Magic number of the served NeoFS network (big-endian 64-bit + // - [**0**] Magic number of the served FrostFS network (big-endian 64-bit // unsigned integer). WRONG_MAGIC_NUMBER = 1; @@ -104,6 +103,11 @@ enum CommonFail { // [**1027**] Node is under maintenance. NODE_UNDER_MAINTENANCE = 3; + + // [**1028**] Invalid argument error. If the server fails on validation of a + // request parameter as the client sent it incorrectly, then this code should + // be used. + INVALID_ARGUMENT = 4; } // Section of statuses for object-related operations. @@ -154,4 +158,4 @@ enum Session { enum APEManager { // [**5120**] The operation is denied by APE manager. APE_MANAGER_ACCESS_DENIED = 0; -} \ No newline at end of file +} diff --git a/protos/src/main/proto/tombstone/types.proto b/protos/src/main/proto/tombstone/types.proto index 9128160..e7164b5 100644 --- a/protos/src/main/proto/tombstone/types.proto +++ b/protos/src/main/proto/tombstone/types.proto @@ -2,16 +2,15 @@ syntax = "proto3"; package neo.fs.v2.tombstone; -option go_package = "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/tombstone/grpc;tombstone"; option java_package = "frostfs.tombstone"; import "refs/types.proto"; // Tombstone keeps record of deleted objects for a few epochs until they are -// purged from the NeoFS network. +// purged from the FrostFS network. message Tombstone { - // Last NeoFS epoch number of the tombstone lifetime. It's set by the - // tombstone creator depending on the current NeoFS network settings. A + // Last FrostFS epoch number of the tombstone lifetime. It's set by the + // tombstone creator depending on the current FrostFS network settings. A // tombstone object must have the same expiration epoch value in // `__SYSTEM__EXPIRATION_EPOCH` (`__NEOFS__EXPIRATION_EPOCH` is deprecated) // attribute. Otherwise, the tombstone will be rejected by a storage node. From 80c7ba58b2e7ef84b342c080e74ae8267fd28b8d Mon Sep 17 00:00:00 2001 From: Ori Bruk Date: Wed, 13 Nov 2024 12:02:41 +0300 Subject: [PATCH 23/38] [#25] Fix validation error Signed-off-by: Ori Bruk --- client/src/main/java/info/frostfs/sdk/utils/Validator.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/src/main/java/info/frostfs/sdk/utils/Validator.java b/client/src/main/java/info/frostfs/sdk/utils/Validator.java index f8e2120..212debd 100644 --- a/client/src/main/java/info/frostfs/sdk/utils/Validator.java +++ b/client/src/main/java/info/frostfs/sdk/utils/Validator.java @@ -16,10 +16,11 @@ import static java.util.Objects.isNull; public class Validator { public static void validate(T object) { - StringBuilder errorMessage = new StringBuilder().append(System.lineSeparator()); + StringBuilder errorMessage = new StringBuilder(); process(object, errorMessage); if (errorMessage.length() != 0) { + errorMessage.insert(0, System.lineSeparator()); throw new ValidationFrostFSException(errorMessage.toString()); } } From aa3cff5a039aa802d02aedc43aa5fa8e6141ad6f Mon Sep 17 00:00:00 2001 From: Ori Bruk Date: Wed, 13 Nov 2024 17:03:00 +0300 Subject: [PATCH 24/38] [#27] Add more modules to CheckStyle Signed-off-by: Ori Bruk --- checkstyle.xml | 10 ++++++++++ .../info/frostfs/sdk/annotations/NotNull.java | 2 +- .../frostfs/sdk/services/ApeManagerClient.java | 2 +- .../frostfs/sdk/services/ContextAccessor.java | 1 - .../sdk/services/impl/SessionToolsImpl.java | 1 - .../sdk/services/impl/interceptor/Labels.java | 15 ++++++++++++--- .../info/frostfs/sdk/tools/RequestSigner.java | 1 - .../java/info/frostfs/sdk/utils/Validator.java | 3 +++ .../java/info/frostfs/sdk/utils/WaitUtil.java | 2 ++ .../info/frostfs/sdk/constants/FilterConst.java | 3 +++ 10 files changed, 32 insertions(+), 8 deletions(-) diff --git a/checkstyle.xml b/checkstyle.xml index 78ea3ce..ebe929e 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -10,6 +10,7 @@ + @@ -20,6 +21,15 @@ + + + + + + + + + diff --git a/client/src/main/java/info/frostfs/sdk/annotations/NotNull.java b/client/src/main/java/info/frostfs/sdk/annotations/NotNull.java index 2780c70..a23bdc4 100644 --- a/client/src/main/java/info/frostfs/sdk/annotations/NotNull.java +++ b/client/src/main/java/info/frostfs/sdk/annotations/NotNull.java @@ -8,4 +8,4 @@ import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface NotNull { -} \ No newline at end of file +} diff --git a/client/src/main/java/info/frostfs/sdk/services/ApeManagerClient.java b/client/src/main/java/info/frostfs/sdk/services/ApeManagerClient.java index 37e4033..5c5263c 100644 --- a/client/src/main/java/info/frostfs/sdk/services/ApeManagerClient.java +++ b/client/src/main/java/info/frostfs/sdk/services/ApeManagerClient.java @@ -11,4 +11,4 @@ public interface ApeManagerClient { void removeChain(Chain chain, ChainTarget chainTarget); List listChains(ChainTarget chainTarget); -} \ No newline at end of file +} diff --git a/client/src/main/java/info/frostfs/sdk/services/ContextAccessor.java b/client/src/main/java/info/frostfs/sdk/services/ContextAccessor.java index 3658609..8663d53 100644 --- a/client/src/main/java/info/frostfs/sdk/services/ContextAccessor.java +++ b/client/src/main/java/info/frostfs/sdk/services/ContextAccessor.java @@ -10,5 +10,4 @@ public class ContextAccessor { public ContextAccessor(ClientEnvironment context) { this.context = context; } - } diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/SessionToolsImpl.java b/client/src/main/java/info/frostfs/sdk/services/impl/SessionToolsImpl.java index e0d3a06..1ee5cbd 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/SessionToolsImpl.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/SessionToolsImpl.java @@ -23,5 +23,4 @@ public class SessionToolsImpl extends ContextAccessor implements SessionTools { return SessionMapper.deserializeSessionToken(sessionToken.getToken()); } - } diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/Labels.java b/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/Labels.java index d026854..ba44eaf 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/Labels.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/Labels.java @@ -9,14 +9,21 @@ import java.util.Collections; import java.util.List; public class Labels { - /** Merges two string lists into an array, maintaining order of first list then second list. */ + private Labels() { + } + + /** + * Merges two string lists into an array, maintaining order of first list then second list. + */ static String[] asArray(List firstList, List secondList) { List list = new ArrayList<>(firstList); list.addAll(secondList); return list.toArray(new String[0]); } - /** Converts a list of strings to a list of grpc metadata keys. */ + /** + * Converts a list of strings to a list of grpc metadata keys. + */ static List> metadataKeys(List headerNames) { List> keys = new ArrayList<>(); for (String name : headerNames) { @@ -41,7 +48,9 @@ public class Labels { return Collections.unmodifiableList(labels); } - /** Adds standard labels, as well as custom ones, in order, to a given collector. */ + /** + * Adds standard labels, as well as custom ones, in order, to a given collector. + */ static T addLabels(SimpleCollector collector, List labels, GrpcMethod method) { List allLabels = new ArrayList<>(); allLabels.add(method.type()); diff --git a/client/src/main/java/info/frostfs/sdk/tools/RequestSigner.java b/client/src/main/java/info/frostfs/sdk/tools/RequestSigner.java index b2ba3d4..a01e4c7 100644 --- a/client/src/main/java/info/frostfs/sdk/tools/RequestSigner.java +++ b/client/src/main/java/info/frostfs/sdk/tools/RequestSigner.java @@ -119,5 +119,4 @@ public class RequestSigner { MessageHelper.setField(verifyBuilder, ORIGIN_SIGNATURE_FIELD_NAME, signMessagePart(key, verifyOrigin)); MessageHelper.setField(request, VERIFY_HEADER_FIELD_NAME, verifyBuilder.build()); } - } diff --git a/client/src/main/java/info/frostfs/sdk/utils/Validator.java b/client/src/main/java/info/frostfs/sdk/utils/Validator.java index 212debd..316f4c8 100644 --- a/client/src/main/java/info/frostfs/sdk/utils/Validator.java +++ b/client/src/main/java/info/frostfs/sdk/utils/Validator.java @@ -15,6 +15,9 @@ import static info.frostfs.sdk.constants.ErrorConst.FIELDS_DELIMITER_COMMA; import static java.util.Objects.isNull; public class Validator { + private Validator() { + } + public static void validate(T object) { StringBuilder errorMessage = new StringBuilder(); process(object, errorMessage); diff --git a/client/src/main/java/info/frostfs/sdk/utils/WaitUtil.java b/client/src/main/java/info/frostfs/sdk/utils/WaitUtil.java index 27e785d..2016c43 100644 --- a/client/src/main/java/info/frostfs/sdk/utils/WaitUtil.java +++ b/client/src/main/java/info/frostfs/sdk/utils/WaitUtil.java @@ -1,6 +1,8 @@ package info.frostfs.sdk.utils; public class WaitUtil { + private WaitUtil() { + } public static void sleep(long ms) { try { diff --git a/models/src/main/java/info/frostfs/sdk/constants/FilterConst.java b/models/src/main/java/info/frostfs/sdk/constants/FilterConst.java index 5340057..5b28055 100644 --- a/models/src/main/java/info/frostfs/sdk/constants/FilterConst.java +++ b/models/src/main/java/info/frostfs/sdk/constants/FilterConst.java @@ -46,4 +46,7 @@ public class FilterConst { // FILTER_HEADER_PHY is a filter key to check if an object physically stored on a node. public static final String FILTER_HEADER_PHY = HEADER_PREFIX + "PHY"; + + private FilterConst() { + } } From 73d5e6d72d78c1aa48dc4b8dfa588c05660c1ff4 Mon Sep 17 00:00:00 2001 From: Ori Bruk Date: Fri, 22 Nov 2024 13:13:30 +0300 Subject: [PATCH 25/38] [#29] Provide a InputStream for the putObject method Signed-off-by: Ori Bruk --- .../java/info/frostfs/sdk/jdo/PutObjectParameters.java | 8 ++++---- .../frostfs/sdk/services/impl/ObjectClientImpl.java | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/client/src/main/java/info/frostfs/sdk/jdo/PutObjectParameters.java b/client/src/main/java/info/frostfs/sdk/jdo/PutObjectParameters.java index d834cd9..b995eac 100644 --- a/client/src/main/java/info/frostfs/sdk/jdo/PutObjectParameters.java +++ b/client/src/main/java/info/frostfs/sdk/jdo/PutObjectParameters.java @@ -6,7 +6,7 @@ import info.frostfs.sdk.dto.session.SessionToken; import lombok.Getter; import lombok.Setter; -import java.io.FileInputStream; +import java.io.InputStream; @Getter @Setter @@ -16,7 +16,7 @@ public class PutObjectParameters { private ObjectHeader header; @NotNull - private FileInputStream payload; + private InputStream payload; private boolean clientCut; private int bufferMaxSize; @@ -26,14 +26,14 @@ public class PutObjectParameters { private long currentStreamPosition; private long fullLength; - public PutObjectParameters(ObjectHeader header, FileInputStream payload, boolean clientCut, int bufferMaxSize) { + public PutObjectParameters(ObjectHeader header, InputStream payload, boolean clientCut, int bufferMaxSize) { this.header = header; this.payload = payload; this.clientCut = clientCut; this.bufferMaxSize = bufferMaxSize; } - public PutObjectParameters(ObjectHeader header, FileInputStream payload) { + public PutObjectParameters(ObjectHeader header, InputStream payload) { this.header = header; this.payload = payload; } diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/ObjectClientImpl.java b/client/src/main/java/info/frostfs/sdk/services/impl/ObjectClientImpl.java index 3979020..f85936c 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/ObjectClientImpl.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/ObjectClientImpl.java @@ -31,8 +31,8 @@ import info.frostfs.sdk.tools.Verifier; import info.frostfs.sdk.utils.Validator; import org.apache.commons.collections4.CollectionUtils; -import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; import java.util.List; @@ -329,17 +329,17 @@ public class ObjectClientImpl extends ContextAccessor implements ObjectClient { return new SearchReader(objectServiceBlockingClient.search(initRequest)); } - private int readNBytes(FileInputStream fileInputStream, byte[] buffer, int size) { + private int readNBytes(InputStream inputStream, byte[] buffer, int size) { try { - return fileInputStream.readNBytes(buffer, 0, size); + return inputStream.readNBytes(buffer, 0, size); } catch (IOException exp) { throw new ProcessFrostFSException(exp.getMessage()); } } - private long getStreamSize(FileInputStream fileInputStream) { + private long getStreamSize(InputStream inputStream) { try { - return fileInputStream.getChannel().size(); + return inputStream.available(); } catch (IOException exp) { throw new ProcessFrostFSException(exp.getMessage()); } From 3c3ed7672795c51092d476eb152780c32ab1c7a0 Mon Sep 17 00:00:00 2001 From: Vitaliy Potyarkin Date: Tue, 10 Dec 2024 18:04:22 +0300 Subject: [PATCH 26/38] [#31] Add CODEOWNERS Signed-off-by: Vitaliy Potyarkin --- CODEOWNERS | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CODEOWNERS diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000..0289d53 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,3 @@ +.* @orikik +.forgejo/.* @potyarkin +Makefile @potyarkin From e9e94807018514e2e4fd175ece8844f1dfccde43 Mon Sep 17 00:00:00 2001 From: Ori Bruk Date: Thu, 16 Jan 2025 19:15:43 +0300 Subject: [PATCH 27/38] [#32] Provide a pool of clients to grpc Signed-off-by: Ori Bruk --- client/pom.xml | 13 +- .../java/info/frostfs/sdk/FrostFSClient.java | 60 +- .../info/frostfs/sdk/constants/PoolConst.java | 14 + .../info/frostfs/sdk/enums/HealthyStatus.java | 23 + .../info/frostfs/sdk/enums/MethodIndex.java | 29 + .../exceptions/ResponseFrostFSException.java | 2 +- .../frostfs/sdk/jdo/ClientEnvironment.java | 8 + .../info/frostfs/sdk/jdo/ClientSettings.java | 6 +- .../frostfs/sdk/jdo/pool/NodeParameters.java | 12 + .../sdk/jdo/pool/PoolInitParameters.java | 44 ++ .../info/frostfs/sdk/pool/ClientStatus.java | 27 + .../frostfs/sdk/pool/ClientStatusMonitor.java | 114 ++++ .../info/frostfs/sdk/pool/ClientWrapper.java | 130 +++++ .../java/info/frostfs/sdk/pool/InnerPool.java | 54 ++ .../info/frostfs/sdk/pool/MethodStatus.java | 31 ++ .../info/frostfs/sdk/pool/NodeStatistic.java | 13 + .../info/frostfs/sdk/pool/NodesParam.java | 19 + .../main/java/info/frostfs/sdk/pool/Pool.java | 520 ++++++++++++++++++ .../frostfs/sdk/pool/RebalanceParameters.java | 15 + .../info/frostfs/sdk/pool/RequestInfo.java | 15 + .../java/info/frostfs/sdk/pool/Sampler.java | 77 +++ .../info/frostfs/sdk/pool/SessionCache.java | 46 ++ .../java/info/frostfs/sdk/pool/Statistic.java | 15 + .../info/frostfs/sdk/pool/StatusSnapshot.java | 13 + .../java/info/frostfs/sdk/pool/WorkList.java | 22 + .../info/frostfs/sdk/pool/WrapperPrm.java | 26 + .../sdk/services/AccountingClient.java | 7 + .../frostfs/sdk/services/CommonClient.java | 5 + .../services/impl/AccountingClientImpl.java | 46 ++ .../sdk/services/impl/ObjectToolsImpl.java | 16 +- .../impl/interceptor/ClientMetrics.java | 12 +- .../services/impl/interceptor/GrpcMethod.java | 12 +- .../sdk/services/impl/interceptor/Labels.java | 1 + .../MonitoringClientInterceptor.java | 10 +- .../info/frostfs/sdk/tools/GrpcClient.java | 15 +- .../frostfs/sdk/utils/FrostFSMessages.java | 20 + .../sdk/services/AccountingClientTest.java | 102 ++++ .../testgenerator/AccountingGenerator.java | 42 ++ cryptography/pom.xml | 4 +- exceptions/pom.xml | 2 +- .../frostfs/sdk/constants/ErrorConst.java | 5 + .../sdk/exceptions/FrostFSException.java | 15 + .../exceptions/ProcessFrostFSException.java | 2 +- .../SessionExpiredFrostFSException.java | 8 + .../SessionNotFoundFrostFSException.java | 8 + .../exceptions/TimeoutFrostFSException.java | 2 +- .../ValidationFrostFSException.java | 2 +- models/pom.xml | 8 +- .../info/frostfs/sdk/constants/AppConst.java | 2 + .../frostfs/sdk/constants/AttributeConst.java | 10 + .../frostfs/sdk/constants/XHeaderConst.java | 7 +- .../frostfs/sdk/dto/container/Container.java | 3 + .../mappers/container/ContainerMapper.java | 30 +- .../container/ContainerMapperTest.java | 23 + pom.xml | 2 +- protos/pom.xml | 2 +- 56 files changed, 1712 insertions(+), 59 deletions(-) create mode 100644 client/src/main/java/info/frostfs/sdk/constants/PoolConst.java create mode 100644 client/src/main/java/info/frostfs/sdk/enums/HealthyStatus.java create mode 100644 client/src/main/java/info/frostfs/sdk/enums/MethodIndex.java create mode 100644 client/src/main/java/info/frostfs/sdk/jdo/pool/NodeParameters.java create mode 100644 client/src/main/java/info/frostfs/sdk/jdo/pool/PoolInitParameters.java create mode 100644 client/src/main/java/info/frostfs/sdk/pool/ClientStatus.java create mode 100644 client/src/main/java/info/frostfs/sdk/pool/ClientStatusMonitor.java create mode 100644 client/src/main/java/info/frostfs/sdk/pool/ClientWrapper.java create mode 100644 client/src/main/java/info/frostfs/sdk/pool/InnerPool.java create mode 100644 client/src/main/java/info/frostfs/sdk/pool/MethodStatus.java create mode 100644 client/src/main/java/info/frostfs/sdk/pool/NodeStatistic.java create mode 100644 client/src/main/java/info/frostfs/sdk/pool/NodesParam.java create mode 100644 client/src/main/java/info/frostfs/sdk/pool/Pool.java create mode 100644 client/src/main/java/info/frostfs/sdk/pool/RebalanceParameters.java create mode 100644 client/src/main/java/info/frostfs/sdk/pool/RequestInfo.java create mode 100644 client/src/main/java/info/frostfs/sdk/pool/Sampler.java create mode 100644 client/src/main/java/info/frostfs/sdk/pool/SessionCache.java create mode 100644 client/src/main/java/info/frostfs/sdk/pool/Statistic.java create mode 100644 client/src/main/java/info/frostfs/sdk/pool/StatusSnapshot.java create mode 100644 client/src/main/java/info/frostfs/sdk/pool/WorkList.java create mode 100644 client/src/main/java/info/frostfs/sdk/pool/WrapperPrm.java create mode 100644 client/src/main/java/info/frostfs/sdk/services/AccountingClient.java create mode 100644 client/src/main/java/info/frostfs/sdk/services/CommonClient.java create mode 100644 client/src/main/java/info/frostfs/sdk/services/impl/AccountingClientImpl.java create mode 100644 client/src/main/java/info/frostfs/sdk/utils/FrostFSMessages.java create mode 100644 client/src/test/java/info/frostfs/sdk/services/AccountingClientTest.java create mode 100644 client/src/test/java/info/frostfs/sdk/testgenerator/AccountingGenerator.java create mode 100644 exceptions/src/main/java/info/frostfs/sdk/exceptions/FrostFSException.java create mode 100644 exceptions/src/main/java/info/frostfs/sdk/exceptions/SessionExpiredFrostFSException.java create mode 100644 exceptions/src/main/java/info/frostfs/sdk/exceptions/SessionNotFoundFrostFSException.java create mode 100644 models/src/main/java/info/frostfs/sdk/constants/AttributeConst.java diff --git a/client/pom.xml b/client/pom.xml index a3f7ff7..e73703d 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -6,7 +6,7 @@ info.frostfs.sdk frostfs-sdk-java - 0.1.0 + 0.2.0 client @@ -21,17 +21,17 @@ info.frostfs.sdk cryptography - 0.1.0 + 0.2.0 info.frostfs.sdk models - 0.1.0 + 0.2.0 info.frostfs.sdk exceptions - 0.1.0 + 0.2.0 commons-codec @@ -54,5 +54,10 @@ simpleclient_common 0.16.0 + + org.slf4j + slf4j-api + 2.0.16 + \ No newline at end of file diff --git a/client/src/main/java/info/frostfs/sdk/FrostFSClient.java b/client/src/main/java/info/frostfs/sdk/FrostFSClient.java index 634c956..cb93dbb 100644 --- a/client/src/main/java/info/frostfs/sdk/FrostFSClient.java +++ b/client/src/main/java/info/frostfs/sdk/FrostFSClient.java @@ -1,5 +1,6 @@ package info.frostfs.sdk; +import frostfs.accounting.Types; import info.frostfs.sdk.dto.chain.Chain; import info.frostfs.sdk.dto.chain.ChainTarget; import info.frostfs.sdk.dto.container.Container; @@ -17,13 +18,16 @@ import info.frostfs.sdk.jdo.ClientEnvironment; import info.frostfs.sdk.jdo.ClientSettings; import info.frostfs.sdk.jdo.NetworkSettings; import info.frostfs.sdk.jdo.PutObjectParameters; -import info.frostfs.sdk.services.*; +import info.frostfs.sdk.pool.SessionCache; +import info.frostfs.sdk.pool.WrapperPrm; +import info.frostfs.sdk.services.CommonClient; import info.frostfs.sdk.services.impl.*; import info.frostfs.sdk.services.impl.interceptor.Configuration; import info.frostfs.sdk.services.impl.interceptor.MonitoringClientInterceptor; import info.frostfs.sdk.utils.Validator; import io.grpc.Channel; import io.grpc.ClientInterceptors; +import io.grpc.ManagedChannel; import java.util.List; @@ -31,26 +35,28 @@ import static info.frostfs.sdk.constants.ErrorConst.VERSION_UNSUPPORTED_TEMPLATE import static info.frostfs.sdk.tools.GrpcClient.initGrpcChannel; import static java.util.Objects.nonNull; -public class FrostFSClient - implements ContainerClient, ObjectClient, ApeManagerClient, NetmapClient, SessionClient, ToolsClient { +public class FrostFSClient implements CommonClient { + private static final MonitoringClientInterceptor MONITORING_CLIENT_INTERCEPTOR = + MonitoringClientInterceptor.create(Configuration.allMetrics()); + private final ContainerClientImpl containerClientImpl; private final ObjectClientImpl objectClientImpl; private final ApeManagerClientImpl apeManagerClient; private final NetmapClientImpl netmapClientImpl; private final SessionClientImpl sessionClientImpl; private final ObjectToolsImpl objectToolsImpl; + private final AccountingClientImpl accountingClient; + private final ManagedChannel channel; public FrostFSClient(ClientSettings clientSettings) { Validator.validate(clientSettings); - Channel channel = nonNull(clientSettings.getChannel()) + this.channel = nonNull(clientSettings.getChannel()) ? clientSettings.getChannel() : initGrpcChannel(clientSettings); - MonitoringClientInterceptor monitoringClientInterceptor = MonitoringClientInterceptor - .create(Configuration.allMetrics()); - channel = ClientInterceptors.intercept(channel, monitoringClientInterceptor); + Channel interceptChannel = ClientInterceptors.intercept(channel, MONITORING_CLIENT_INTERCEPTOR); ClientEnvironment clientEnvironment = - new ClientEnvironment(clientSettings.getKey(), channel, new Version(), this); + new ClientEnvironment(clientSettings.getKey(), interceptChannel, new Version(), this); Validator.validate(clientEnvironment); @@ -60,10 +66,30 @@ public class FrostFSClient this.netmapClientImpl = new NetmapClientImpl(clientEnvironment); this.sessionClientImpl = new SessionClientImpl(clientEnvironment); this.objectToolsImpl = new ObjectToolsImpl(clientEnvironment); - checkFrostFsVersionSupport(clientEnvironment.getVersion()); + this.accountingClient = new AccountingClientImpl(clientEnvironment); + checkFrostFSVersionSupport(clientEnvironment.getVersion()); } - private void checkFrostFsVersionSupport(Version version) { + public FrostFSClient(WrapperPrm prm, SessionCache cache) { + this.channel = initGrpcChannel(prm.getAddress()); + + Channel interceptChannel = ClientInterceptors.intercept(channel, MONITORING_CLIENT_INTERCEPTOR); + ClientEnvironment clientEnvironment = + new ClientEnvironment(prm.getKey(), interceptChannel, new Version(), this); + + Validator.validate(clientEnvironment); + + this.containerClientImpl = new ContainerClientImpl(clientEnvironment); + this.objectClientImpl = new ObjectClientImpl(clientEnvironment); + this.apeManagerClient = new ApeManagerClientImpl(clientEnvironment); + this.netmapClientImpl = new NetmapClientImpl(clientEnvironment); + this.sessionClientImpl = new SessionClientImpl(clientEnvironment); + this.objectToolsImpl = new ObjectToolsImpl(clientEnvironment); + this.accountingClient = new AccountingClientImpl(clientEnvironment); + checkFrostFSVersionSupport(clientEnvironment.getVersion()); + } + + private void checkFrostFSVersionSupport(Version version) { var localNodeInfo = netmapClientImpl.getLocalNodeInfo(); if (!localNodeInfo.getVersion().isSupported(version)) { throw new ProcessFrostFSException( @@ -165,4 +191,18 @@ public class FrostFSClient public ObjectId calculateObjectId(ObjectHeader header) { return objectToolsImpl.calculateObjectId(header); } + + @Override + public Types.Decimal getBalance() { + return accountingClient.getBalance(); + } + + public String dial() { + accountingClient.getBalance(); + return null; + } + + public void close() { + channel.shutdown(); + } } diff --git a/client/src/main/java/info/frostfs/sdk/constants/PoolConst.java b/client/src/main/java/info/frostfs/sdk/constants/PoolConst.java new file mode 100644 index 0000000..30d853f --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/constants/PoolConst.java @@ -0,0 +1,14 @@ +package info.frostfs.sdk.constants; + +public class PoolConst { + public static final int DEFAULT_SESSION_TOKEN_EXPIRATION_DURATION = 100; // in epochs + public static final int DEFAULT_ERROR_THRESHOLD = 100; + public static final int DEFAULT_GRACEFUL_CLOSE_ON_SWITCH_TIMEOUT = 10; // Seconds + public static final int DEFAULT_REBALANCE_INTERVAL = 15; // Seconds + public static final int DEFAULT_HEALTHCHECK_TIMEOUT = 4; // Seconds + public static final int DEFAULT_DIAL_TIMEOUT = 5; // Seconds + public static final int DEFAULT_STREAM_TIMEOUT = 10; // Seconds + + private PoolConst() { + } +} diff --git a/client/src/main/java/info/frostfs/sdk/enums/HealthyStatus.java b/client/src/main/java/info/frostfs/sdk/enums/HealthyStatus.java new file mode 100644 index 0000000..f88a8db --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/enums/HealthyStatus.java @@ -0,0 +1,23 @@ +package info.frostfs.sdk.enums; + +public enum HealthyStatus { + // status HEALTHY is set when connection is ready to be used by the pool. + HEALTHY(1), + + // status UNHEALTHY_ON_REQUEST is set when communication after dialing to the + // endpoint is failed due to immediate or accumulated errors, connection is + // available and pool should close it before re-establishing connection once again. + UNHEALTHY_ON_REQUEST(2), + + // status UNHEALTHY_ON_DIAL is set when dialing to the endpoint is failed, + // so there is no connection to the endpoint, and pool should not close it + // before re-establishing connection once again. + UNHEALTHY_ON_DIAL(3), + ; + + public final int value; + + HealthyStatus(int value) { + this.value = value; + } +} diff --git a/client/src/main/java/info/frostfs/sdk/enums/MethodIndex.java b/client/src/main/java/info/frostfs/sdk/enums/MethodIndex.java new file mode 100644 index 0000000..af8f4a5 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/enums/MethodIndex.java @@ -0,0 +1,29 @@ +package info.frostfs.sdk.enums; + +public enum MethodIndex { + METHOD_BALANCE_GET("balanceGet"), + METHOD_CONTAINER_PUT("containerPut"), + METHOD_CONTAINER_GET("ContainerGet"), + METHOD_CONTAINER_LIST("ContainerList"), + METHOD_CONTAINER_DELETE("ContainerDelete"), + METHOD_ENDPOINT_INFO("EndpointInfo"), + METHOD_NETWORK_INFO("NetworkInfo"), + METHOD_NETMAP_SNAPSHOT("NetMapSnapshot"), + METHOD_OBJECT_PUT("ObjectPut"), + METHOD_OBJECT_DELETE("ObjectDelete"), + METHOD_OBJECT_GET("ObjectGet"), + METHOD_OBJECT_HEAD("ObjectHead"), + METHOD_OBJECT_RANGE("ObjectRange"), + METHOD_OBJECT_PATCH("ObjectPatch"), + METHOD_SESSION_CREATE("SessionCreate"), + METHOD_APE_MANAGER_ADD_CHAIN("APEManagerAddChain"), + METHOD_APE_MANAGER_REMOVE_CHAIN("APEManagerRemoveChain"), + METHOD_APE_MANAGER_LIST_CHAINS("APEManagerListChains"), + ; + + public final String methodName; + + MethodIndex(String methodName) { + this.methodName = methodName; + } +} diff --git a/client/src/main/java/info/frostfs/sdk/exceptions/ResponseFrostFSException.java b/client/src/main/java/info/frostfs/sdk/exceptions/ResponseFrostFSException.java index 6b216a6..c266395 100644 --- a/client/src/main/java/info/frostfs/sdk/exceptions/ResponseFrostFSException.java +++ b/client/src/main/java/info/frostfs/sdk/exceptions/ResponseFrostFSException.java @@ -4,7 +4,7 @@ import info.frostfs.sdk.dto.response.ResponseStatus; import lombok.Getter; @Getter -public class ResponseFrostFSException extends RuntimeException { +public class ResponseFrostFSException extends FrostFSException { private final ResponseStatus status; public ResponseFrostFSException(ResponseStatus status) { diff --git a/client/src/main/java/info/frostfs/sdk/jdo/ClientEnvironment.java b/client/src/main/java/info/frostfs/sdk/jdo/ClientEnvironment.java index 34eca67..015ba6d 100644 --- a/client/src/main/java/info/frostfs/sdk/jdo/ClientEnvironment.java +++ b/client/src/main/java/info/frostfs/sdk/jdo/ClientEnvironment.java @@ -38,4 +38,12 @@ public class ClientEnvironment { this.channel = channel; this.frostFSClient = frostFSClient; } + + public ClientEnvironment(ECDsa key, Channel channel, Version version, FrostFSClient frostFSClient) { + this.key = key; + this.ownerId = new OwnerId(key.getPublicKeyByte()); + this.version = version; + this.channel = channel; + this.frostFSClient = frostFSClient; + } } diff --git a/client/src/main/java/info/frostfs/sdk/jdo/ClientSettings.java b/client/src/main/java/info/frostfs/sdk/jdo/ClientSettings.java index bca70ce..3f8d2df 100644 --- a/client/src/main/java/info/frostfs/sdk/jdo/ClientSettings.java +++ b/client/src/main/java/info/frostfs/sdk/jdo/ClientSettings.java @@ -2,8 +2,8 @@ package info.frostfs.sdk.jdo; import info.frostfs.sdk.annotations.AtLeastOneIsFilled; import info.frostfs.sdk.annotations.NotNull; -import io.grpc.Channel; import io.grpc.ChannelCredentials; +import io.grpc.ManagedChannel; import lombok.Getter; import lombok.experimental.FieldNameConstants; @@ -17,7 +17,7 @@ public class ClientSettings { private String host; private ChannelCredentials credentials; - private Channel channel; + private ManagedChannel channel; public ClientSettings(String key, String host) { this.key = key; @@ -30,7 +30,7 @@ public class ClientSettings { this.credentials = credentials; } - public ClientSettings(String key, Channel channel) { + public ClientSettings(String key, ManagedChannel channel) { this.key = key; this.channel = channel; } diff --git a/client/src/main/java/info/frostfs/sdk/jdo/pool/NodeParameters.java b/client/src/main/java/info/frostfs/sdk/jdo/pool/NodeParameters.java new file mode 100644 index 0000000..01c9df0 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/jdo/pool/NodeParameters.java @@ -0,0 +1,12 @@ +package info.frostfs.sdk.jdo.pool; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class NodeParameters { + private final int priority; + private final String address; + private final double weight; +} diff --git a/client/src/main/java/info/frostfs/sdk/jdo/pool/PoolInitParameters.java b/client/src/main/java/info/frostfs/sdk/jdo/pool/PoolInitParameters.java new file mode 100644 index 0000000..71eb670 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/jdo/pool/PoolInitParameters.java @@ -0,0 +1,44 @@ +package info.frostfs.sdk.jdo.pool; + +import info.frostfs.sdk.jdo.ECDsa; +import info.frostfs.sdk.pool.ClientWrapper; +import io.grpc.ClientInterceptors; +import io.netty.channel.ChannelOption; +import lombok.Getter; +import lombok.Setter; +import org.slf4j.Logger; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.function.Function; + +@Getter +@Setter +public class PoolInitParameters { + private ECDsa key; + + private long nodeDialTimeout; + + private long nodeStreamTimeout; + + private long healthCheckTimeout; + + private long clientRebalanceInterval; + + private long sessionExpirationDuration; + + private int errorThreshold; + + private NodeParameters[] nodeParams; + + private ChannelOption[] dialOptions; + + private Function clientBuilder; + + private long gracefulCloseOnSwitchTimeout; + + private Logger logger; + + private Collection interceptors = new ArrayList<>(); +} + diff --git a/client/src/main/java/info/frostfs/sdk/pool/ClientStatus.java b/client/src/main/java/info/frostfs/sdk/pool/ClientStatus.java new file mode 100644 index 0000000..53b04c1 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/pool/ClientStatus.java @@ -0,0 +1,27 @@ +package info.frostfs.sdk.pool; + +public interface ClientStatus { + // isHealthy checks if the connection can handle requests. + boolean isHealthy(); + + // isDialed checks if the connection was created. + boolean isDialed(); + + // setUnhealthy marks client as unhealthy. + void setUnhealthy(); + + // address return address of endpoint. + String getAddress(); + + // currentErrorRate returns current errors rate. + // After specific threshold connection is considered as unhealthy. + // Pool.startRebalance routine can make this connection healthy again. + int getCurrentErrorRate(); + + // overallErrorRate returns the number of all happened errors. + long getOverallErrorRate(); + + // methodsStatus returns statistic for all used methods. + StatusSnapshot[] getMethodsStatus(); +} + diff --git a/client/src/main/java/info/frostfs/sdk/pool/ClientStatusMonitor.java b/client/src/main/java/info/frostfs/sdk/pool/ClientStatusMonitor.java new file mode 100644 index 0000000..0770482 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/pool/ClientStatusMonitor.java @@ -0,0 +1,114 @@ +package info.frostfs.sdk.pool; + +import info.frostfs.sdk.enums.HealthyStatus; +import info.frostfs.sdk.enums.MethodIndex; +import info.frostfs.sdk.utils.FrostFSMessages; +import lombok.Getter; +import lombok.Setter; +import org.slf4j.Logger; + +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.ReentrantLock; + +@Getter +@Setter +public class ClientStatusMonitor implements ClientStatus { + private final ReentrantLock lock = new ReentrantLock(); + private final Logger logger; + private final AtomicInteger healthy = new AtomicInteger(); + + private final String address; + private final MethodStatus[] methods; + + private int errorThreshold; + private int currentErrorCount; + private long overallErrorCount; + + public ClientStatusMonitor(Logger logger, String address) { + this.logger = logger; + this.healthy.set(HealthyStatus.HEALTHY.value); + + this.address = address; + this.methods = Arrays.stream(MethodIndex.values()) + .map(t -> new MethodStatus(t.methodName)) + .toArray(MethodStatus[]::new); + } + + @Override + public boolean isHealthy() { + return healthy.get() == HealthyStatus.HEALTHY.value; + } + + @Override + public boolean isDialed() { + return healthy.get() != HealthyStatus.UNHEALTHY_ON_DIAL.value; + } + + public void setHealthy() { + healthy.set(HealthyStatus.HEALTHY.ordinal()); + } + + @Override + public void setUnhealthy() { + healthy.set(HealthyStatus.UNHEALTHY_ON_REQUEST.value); + } + + public void setUnhealthyOnDial() { + healthy.set(HealthyStatus.UNHEALTHY_ON_DIAL.value); + } + + public void incErrorRate() { + boolean thresholdReached; + lock.lock(); + try { + currentErrorCount++; + overallErrorCount++; + + thresholdReached = currentErrorCount >= errorThreshold; + + if (thresholdReached) { + setUnhealthy(); + currentErrorCount = 0; + } + } finally { + lock.unlock(); + } + + if (thresholdReached && logger != null) { + FrostFSMessages.errorThresholdReached(logger, address, errorThreshold); + } + } + + @Override + public int getCurrentErrorRate() { + lock.lock(); + try { + return currentErrorCount; + } finally { + lock.unlock(); + } + } + + @Override + public long getOverallErrorRate() { + lock.lock(); + try { + return overallErrorCount; + } finally { + lock.unlock(); + } + } + + @Override + public StatusSnapshot[] getMethodsStatus() { + StatusSnapshot[] result = new StatusSnapshot[methods.length]; + + for (int i = 0; i < result.length; i++) { + result[i] = methods[i].getSnapshot(); + } + + return result; + } +} + diff --git a/client/src/main/java/info/frostfs/sdk/pool/ClientWrapper.java b/client/src/main/java/info/frostfs/sdk/pool/ClientWrapper.java new file mode 100644 index 0000000..0189cc1 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/pool/ClientWrapper.java @@ -0,0 +1,130 @@ +package info.frostfs.sdk.pool; + +import info.frostfs.sdk.FrostFSClient; +import info.frostfs.sdk.enums.MethodIndex; +import info.frostfs.sdk.exceptions.ResponseFrostFSException; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; +import info.frostfs.sdk.utils.WaitUtil; +import lombok.AccessLevel; +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import static info.frostfs.sdk.constants.ErrorConst.POOL_CLIENT_UNHEALTHY; + +@Getter +public class ClientWrapper extends ClientStatusMonitor { + @Getter(value = AccessLevel.NONE) + private final Lock lock = new ReentrantLock(); + private final SessionCache sessionCache; + private final WrapperPrm wrapperPrm; + private FrostFSClient client; + + public ClientWrapper(WrapperPrm wrapperPrm, Pool pool) { + super(wrapperPrm.getLogger(), wrapperPrm.getAddress()); + this.wrapperPrm = wrapperPrm; + setErrorThreshold(wrapperPrm.getErrorThreshold()); + + this.sessionCache = pool.getSessionCache(); + this.client = new FrostFSClient(wrapperPrm, sessionCache); + } + + + public FrostFSClient getClient() { + lock.lock(); + try { + if (isHealthy()) { + return client; + } + return null; + } finally { + lock.unlock(); + } + } + + public void dial() { + FrostFSClient client = getClient(); + if (client == null) { + throw new ValidationFrostFSException(POOL_CLIENT_UNHEALTHY); + } + client.dial(); + } + + public void handleError(Exception exp) { + if (exp instanceof ResponseFrostFSException && ((ResponseFrostFSException) exp).getStatus() != null) { + switch (((ResponseFrostFSException) exp).getStatus().getCode()) { + case INTERNAL: + case WRONG_MAGIC_NUMBER: + case SIGNATURE_VERIFICATION_FAILURE: + case NODE_UNDER_MAINTENANCE: + incErrorRate(); + } + return; + } + + incErrorRate(); + } + + private void scheduleGracefulClose() { + if (client == null) { + return; + } + + WaitUtil.sleep(wrapperPrm.getGracefulCloseOnSwitchTimeout()); + client.close(); + } + + public CompletableFuture restartIfUnhealthy() { + try { + client.getLocalNodeInfo(); + return CompletableFuture.completedFuture(false); + } catch (Exception ignored) { + } + + if (isDialed()) { + scheduleGracefulClose(); + } + + return CompletableFuture.completedFuture(restartClient()); + } + + private boolean restartClient() { + FrostFSClient newClient = null; + try { + newClient = new FrostFSClient(wrapperPrm, sessionCache); + + var error = newClient.dial(); + if (StringUtils.isNotBlank(error)) { + setUnhealthyOnDial(); + newClient.close(); + return true; + } + + lock.lock(); + client = newClient; + lock.unlock(); + } catch (Exception exp) { + if (newClient != null) { + newClient.close(); + } + } + + try { + client.getLocalNodeInfo(); + } catch (Exception exp) { + setUnhealthy(); + return true; + } + + setHealthy(); + return false; + } + + public void incRequests(long elapsed, MethodIndex method) { + var methodStat = getMethods()[method.ordinal()]; + methodStat.incRequests(elapsed); + } +} diff --git a/client/src/main/java/info/frostfs/sdk/pool/InnerPool.java b/client/src/main/java/info/frostfs/sdk/pool/InnerPool.java new file mode 100644 index 0000000..94a8be4 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/pool/InnerPool.java @@ -0,0 +1,54 @@ +package info.frostfs.sdk.pool; + +import java.util.concurrent.locks.ReentrantLock; + +class InnerPool { + private static final int ATTEMPTS_COUNT = 3; + private final ReentrantLock lock = new ReentrantLock(); + private final ClientWrapper[] clients; + private Sampler sampler; + + InnerPool(Sampler sampler, ClientWrapper[] clients) { + this.sampler = sampler; + this.clients = clients; + } + + Sampler getSampler() { + return sampler; + } + + void setSampler(Sampler sampler) { + this.sampler = sampler; + } + + ClientWrapper[] getClients() { + return clients; + } + + ClientWrapper connection() { + lock.lock(); + try { + if (clients.length == 1) { + ClientWrapper client = clients[0]; + if (client.isHealthy()) { + return client; + } + } else { + int attempts = ATTEMPTS_COUNT * clients.length; + + for (int i = 0; i < attempts; i++) { + int index = sampler.next(); + + if (clients[index].isHealthy()) { + return clients[index]; + } + } + } + + return null; + } finally { + lock.unlock(); + } + } +} + diff --git a/client/src/main/java/info/frostfs/sdk/pool/MethodStatus.java b/client/src/main/java/info/frostfs/sdk/pool/MethodStatus.java new file mode 100644 index 0000000..5309c93 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/pool/MethodStatus.java @@ -0,0 +1,31 @@ +package info.frostfs.sdk.pool; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; + +import java.util.concurrent.locks.ReentrantLock; + +@Setter +@Getter +public class MethodStatus { + @Getter(AccessLevel.NONE) + private final ReentrantLock lock = new ReentrantLock(); + private final String name; + private StatusSnapshot snapshot; + + public MethodStatus(String name) { + this.name = name; + this.snapshot = new StatusSnapshot(); + } + + void incRequests(long elapsed) { + lock.lock(); + try { + snapshot.setAllTime(snapshot.getAllTime() + elapsed); + snapshot.setAllRequests(snapshot.getAllRequests() + 1); + } finally { + lock.unlock(); + } + } +} diff --git a/client/src/main/java/info/frostfs/sdk/pool/NodeStatistic.java b/client/src/main/java/info/frostfs/sdk/pool/NodeStatistic.java new file mode 100644 index 0000000..2ad4cdc --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/pool/NodeStatistic.java @@ -0,0 +1,13 @@ +package info.frostfs.sdk.pool; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class NodeStatistic { + private String address; + private StatusSnapshot[] methods; + private long overallErrors; + private int currentErrors; +} diff --git a/client/src/main/java/info/frostfs/sdk/pool/NodesParam.java b/client/src/main/java/info/frostfs/sdk/pool/NodesParam.java new file mode 100644 index 0000000..0221852 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/pool/NodesParam.java @@ -0,0 +1,19 @@ +package info.frostfs.sdk.pool; + +import lombok.Getter; + +import java.util.ArrayList; +import java.util.List; + +@Getter +public class NodesParam { + private final int priority; + private final List address; + private final List weight; + + public NodesParam(int priority) { + this.priority = priority; + this.address = new ArrayList<>(); + this.weight = new ArrayList<>(); + } +} diff --git a/client/src/main/java/info/frostfs/sdk/pool/Pool.java b/client/src/main/java/info/frostfs/sdk/pool/Pool.java new file mode 100644 index 0000000..3306ea5 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/pool/Pool.java @@ -0,0 +1,520 @@ +package info.frostfs.sdk.pool; + +import frostfs.refs.Types; +import info.frostfs.sdk.dto.chain.Chain; +import info.frostfs.sdk.dto.chain.ChainTarget; +import info.frostfs.sdk.dto.container.Container; +import info.frostfs.sdk.dto.container.ContainerId; +import info.frostfs.sdk.dto.netmap.NetmapSnapshot; +import info.frostfs.sdk.dto.netmap.NodeInfo; +import info.frostfs.sdk.dto.object.*; +import info.frostfs.sdk.dto.session.SessionToken; +import info.frostfs.sdk.exceptions.FrostFSException; +import info.frostfs.sdk.exceptions.SessionExpiredFrostFSException; +import info.frostfs.sdk.exceptions.SessionNotFoundFrostFSException; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; +import info.frostfs.sdk.jdo.ECDsa; +import info.frostfs.sdk.jdo.NetworkSettings; +import info.frostfs.sdk.jdo.PutObjectParameters; +import info.frostfs.sdk.jdo.pool.NodeParameters; +import info.frostfs.sdk.jdo.pool.PoolInitParameters; +import info.frostfs.sdk.services.CommonClient; +import info.frostfs.sdk.utils.FrostFSMessages; +import info.frostfs.sdk.utils.WaitUtil; +import lombok.Getter; +import org.slf4j.Logger; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.ReentrantLock; +import java.util.function.Function; + +import static info.frostfs.sdk.Helper.getHexString; +import static info.frostfs.sdk.constants.ErrorConst.*; +import static info.frostfs.sdk.constants.PoolConst.*; + +@Getter +public class Pool implements CommonClient { + + private final ReentrantLock lock = new ReentrantLock(); + private final ECDsa key; + private final SessionCache sessionCache; + private final long sessionTokenDuration; + private final RebalanceParameters rebalanceParams; + private final Function clientBuilder; + private final Logger logger; + private InnerPool[] innerPools; + private Types.OwnerID ownerID; + private OwnerId ownerId; + private boolean disposedValue; + private long maxObjectSize; + private ClientStatus clientStatus; + + public Pool(PoolInitParameters options) { + if (options == null || options.getKey() == null) { + throw new ValidationFrostFSException( + String.format( + PARAMS_ARE_MISSING_TEMPLATE, + String.join( + FIELDS_DELIMITER_COMMA, PoolInitParameters.class.getName(), ECDsa.class.getName() + ) + ) + ); + } + + List nodesParams = adjustNodeParams(options.getNodeParams()); + SessionCache cache = new SessionCache(options.getSessionExpirationDuration()); + fillDefaultInitParams(options, this); + + this.key = options.getKey(); + this.sessionCache = cache; + this.logger = options.getLogger(); + this.sessionTokenDuration = options.getSessionExpirationDuration(); + + this.rebalanceParams = new RebalanceParameters( + nodesParams.toArray(new NodesParam[0]), + options.getHealthCheckTimeout(), + options.getClientRebalanceInterval(), + options.getSessionExpirationDuration()); + + this.clientBuilder = options.getClientBuilder(); + } + + private static List adjustNodeParams(NodeParameters[] nodeParams) { + if (nodeParams == null || nodeParams.length == 0) { + throw new ValidationFrostFSException(POOL_PEERS_IS_MISSING); + } + + Map nodesParamsDict = new HashMap<>(nodeParams.length); + for (NodeParameters nodeParam : nodeParams) { + var nodesParam = nodesParamsDict + .computeIfAbsent(nodeParam.getPriority(), k -> new NodesParam(nodeParam.getPriority())); + nodesParam.getAddress().add(nodeParam.getAddress()); + nodesParam.getWeight().add(nodeParam.getWeight()); + } + + List nodesParams = new ArrayList<>(nodesParamsDict.values()); + nodesParams.sort(Comparator.comparingInt(NodesParam::getPriority)); + + for (NodesParam nodes : nodesParams) { + double[] newWeights = adjustWeights(nodes.getWeight().stream().mapToDouble(Double::doubleValue).toArray()); + nodes.getWeight().clear(); + for (double weight : newWeights) { + nodes.getWeight().add(weight); + } + } + + return nodesParams; + } + + private static double[] adjustWeights(double[] weights) { + double[] adjusted = new double[weights.length]; + double sum = Arrays.stream(weights).sum(); + + if (sum > 0) { + for (int i = 0; i < weights.length; i++) { + adjusted[i] = weights[i] / sum; + } + } + + return adjusted; + } + + private static void fillDefaultInitParams(PoolInitParameters parameters, Pool pool) { + if (parameters.getSessionExpirationDuration() == 0) { + parameters.setSessionExpirationDuration(DEFAULT_SESSION_TOKEN_EXPIRATION_DURATION); + } + + if (parameters.getErrorThreshold() == 0) { + parameters.setErrorThreshold(DEFAULT_ERROR_THRESHOLD); + } + + if (parameters.getClientRebalanceInterval() <= 0) { + parameters.setClientRebalanceInterval(DEFAULT_REBALANCE_INTERVAL); + } + + if (parameters.getGracefulCloseOnSwitchTimeout() <= 0) { + parameters.setGracefulCloseOnSwitchTimeout(DEFAULT_GRACEFUL_CLOSE_ON_SWITCH_TIMEOUT); + } + + if (parameters.getHealthCheckTimeout() <= 0) { + parameters.setHealthCheckTimeout(DEFAULT_HEALTHCHECK_TIMEOUT); + } + + if (parameters.getNodeDialTimeout() <= 0) { + parameters.setNodeDialTimeout(DEFAULT_DIAL_TIMEOUT); + } + + if (parameters.getNodeStreamTimeout() <= 0) { + parameters.setNodeStreamTimeout(DEFAULT_STREAM_TIMEOUT); + } + + if (parameters.getSessionExpirationDuration() == 0) { + parameters.setSessionExpirationDuration(DEFAULT_SESSION_TOKEN_EXPIRATION_DURATION); + } + + if (parameters.getClientBuilder() == null) { + parameters.setClientBuilder(address -> { + WrapperPrm wrapperPrm = new WrapperPrm(); + wrapperPrm.setAddress(address); + wrapperPrm.setKey(parameters.getKey()); + wrapperPrm.setLogger(parameters.getLogger()); + wrapperPrm.setDialTimeout(parameters.getNodeDialTimeout()); + wrapperPrm.setStreamTimeout(parameters.getNodeStreamTimeout()); + wrapperPrm.setErrorThreshold(parameters.getErrorThreshold()); + wrapperPrm.setGracefulCloseOnSwitchTimeout(parameters.getGracefulCloseOnSwitchTimeout()); + wrapperPrm.setInterceptors(parameters.getInterceptors()); + return new ClientWrapper(wrapperPrm, pool); + }); + } + } + + private static SessionToken initSessionForDuration(ClientWrapper cw, long duration) { + var client = cw.getClient(); + NetworkSettings networkInfo = client.getNetworkSettings(); + + long epoch = networkInfo.getEpochDuration(); + long exp = (Long.MAX_VALUE - epoch < duration) ? Long.MAX_VALUE : (epoch + duration); + + return client.createSession(exp); + } + + private static String formCacheKey(String address, String key) { + return address + key; + } + + public String dial() { + InnerPool[] inner = new InnerPool[rebalanceParams.getNodesParams().length]; + boolean atLeastOneHealthy = false; + int i = 0; + + for (NodesParam nodeParams : rebalanceParams.getNodesParams()) { + ClientWrapper[] clients = new ClientWrapper[nodeParams.getWeight().size()]; + + for (int j = 0; j < nodeParams.getAddress().size(); j++) { + ClientWrapper client = clients[j] = clientBuilder.apply(nodeParams.getAddress().get(j)); + boolean dialed = false; + + try { + client.dial(); + dialed = true; + + SessionToken token = initSessionForDuration(client, rebalanceParams.getSessionExpirationDuration()); + String cacheKey = formCacheKey( + nodeParams.getAddress().get(j), + getHexString(key.getPublicKeyByte()) + ); + sessionCache.setValue(cacheKey, token); + + atLeastOneHealthy = true; + } catch (ValidationFrostFSException exp) { + break; + } catch (Exception exp) { + if (!dialed) { + client.setUnhealthyOnDial(); + } else { + client.setUnhealthy(); + } + + if (logger != null) { + FrostFSMessages + .sessionCreationError(logger, client.getWrapperPrm().getAddress(), exp.getMessage()); + } + } + } + + Sampler sampler = new Sampler(nodeParams.getWeight().stream().mapToDouble(Double::doubleValue).toArray()); + inner[i] = new InnerPool(sampler, clients); + i++; + } + + if (!atLeastOneHealthy) { + return POOL_NODES_UNHEALTHY; + } + + this.innerPools = inner; + + NetworkSettings networkSettings = getNetworkSettings(); + + this.maxObjectSize = networkSettings.getMaxObjectSize(); + startRebalance(); + + return null; + } + + private ClientWrapper connection() { + for (InnerPool pool : innerPools) { + ClientWrapper client = pool.connection(); + if (client != null) { + return client; + } + } + + throw new FrostFSException(POOL_CLIENTS_UNHEALTHY); + } + + public void close() { + if (innerPools != null) { + for (InnerPool innerPool : innerPools) { + for (ClientWrapper client : innerPool.getClients()) { + if (client.isDialed()) { + client.getClient().close(); + } + } + } + } + } + + public void startRebalance() { + double[][] buffers = new double[rebalanceParams.getNodesParams().length][]; + + for (int i = 0; i < rebalanceParams.getNodesParams().length; i++) { + NodesParam parameters = rebalanceParams.getNodesParams()[i]; + buffers[i] = new double[parameters.getWeight().size()]; + + CompletableFuture.runAsync(() -> { + WaitUtil.sleep(rebalanceParams.getClientRebalanceInterval()); + updateNodesHealth(buffers); + }); + } + } + + private void updateNodesHealth(double[][] buffers) { + CompletableFuture[] tasks = new CompletableFuture[innerPools.length]; + + for (int i = 0; i < innerPools.length; i++) { + double[] bufferWeights = buffers[i]; + int finalI = i; + tasks[i] = CompletableFuture.runAsync(() -> updateInnerNodesHealth(finalI, bufferWeights)); + } + + CompletableFuture.allOf(tasks).join(); + } + + private void updateInnerNodesHealth(int poolIndex, double[] bufferWeights) { + if (poolIndex > innerPools.length - 1) { + return; + } + + InnerPool pool = innerPools[poolIndex]; + RebalanceParameters options = rebalanceParams; + int[] healthyChanged = {0}; + + CompletableFuture[] tasks = new CompletableFuture[pool.getClients().length]; + + for (int j = 0; j < pool.getClients().length; j++) { + ClientWrapper client = pool.getClients()[j]; + AtomicBoolean healthy = new AtomicBoolean(false); + AtomicReference error = new AtomicReference<>(); + AtomicBoolean changed = new AtomicBoolean(false); + + int finalJ = j; + tasks[j] = client.restartIfUnhealthy().handle((unused, throwable) -> { + if (throwable != null) { + error.set(throwable.getMessage()); + bufferWeights[finalJ] = 0; + sessionCache.deleteByPrefix(client.getAddress()); + } else { + changed.set(unused); + healthy.set(true); + bufferWeights[finalJ] = options.getNodesParams()[poolIndex].getWeight().get(finalJ); + } + return null; + }).thenRun(() -> { + if (changed.get()) { + if (error.get() != null && logger != null) { + FrostFSMessages.healthChanged(logger, client.getAddress(), healthy.get(), error.get()); + } + healthyChanged[0] = 1; + } + }); + } + + CompletableFuture.allOf(tasks).thenRun(() -> { + if (healthyChanged[0] == 1) { + double[] probabilities = adjustWeights(bufferWeights); + lock.lock(); + try { + pool.setSampler(new Sampler(probabilities)); + } finally { + lock.unlock(); + } + } + }); + } + + private boolean checkSessionTokenErr(Exception error, String address) { + if (error == null) { + return false; + } + + if (error instanceof SessionNotFoundFrostFSException || error instanceof SessionExpiredFrostFSException) { + sessionCache.deleteByPrefix(address); + return true; + } + + return false; + } + + public Statistic statistic() { + if (innerPools == null) { + throw new ValidationFrostFSException(POOL_NOT_DIALED); + } + + Statistic statistics = new Statistic(); + + for (InnerPool inner : innerPools) { + int valueIndex = 0; + String[] nodes = new String[inner.getClients().length]; + + lock.lock(); + try { + for (ClientWrapper client : inner.getClients()) { + if (client.isHealthy()) { + nodes[valueIndex] = client.getAddress(); + } + + NodeStatistic node = new NodeStatistic(); + node.setAddress(client.getAddress()); + node.setMethods(client.getMethodsStatus()); + node.setOverallErrors(client.getOverallErrorRate()); + node.setCurrentErrors(client.getCurrentErrorRate()); + + statistics.getNodes().add(node); + valueIndex++; + statistics.setOverallErrors(statistics.getOverallErrors() + node.getOverallErrors()); + } + + if (statistics.getCurrentNodes() == null || statistics.getCurrentNodes().length == 0) { + statistics.setCurrentNodes(nodes); + } + } finally { + lock.unlock(); + } + } + + return statistics; + } + + + @Override + public Container getContainer(ContainerId cid) { + ClientWrapper client = connection(); + return client.getClient().getContainer(cid); + } + + @Override + public List listContainers() { + ClientWrapper client = connection(); + return client.getClient().listContainers(); + } + + @Override + public ContainerId createContainer(Container container) { + ClientWrapper client = connection(); + return client.getClient().createContainer(container); + } + + @Override + public void deleteContainer(ContainerId cid) { + ClientWrapper client = connection(); + client.getClient().deleteContainer(cid); + } + + @Override + public ObjectHeader getObjectHead(ContainerId containerId, ObjectId objectId) { + ClientWrapper client = connection(); + return client.getClient().getObjectHead(containerId, objectId); + } + + @Override + public ObjectFrostFS getObject(ContainerId containerId, ObjectId objectId) { + ClientWrapper client = connection(); + return client.getClient().getObject(containerId, objectId); + } + + @Override + public ObjectId putObject(PutObjectParameters parameters) { + ClientWrapper client = connection(); + return client.getClient().putObject(parameters); + } + + @Override + public ObjectId putSingleObject(ObjectFrostFS objectFrostFS) { + ClientWrapper client = connection(); + return client.getClient().putSingleObject(objectFrostFS); + } + + @Override + public void deleteObject(ContainerId containerId, ObjectId objectId) { + ClientWrapper client = connection(); + client.getClient().deleteObject(containerId, objectId); + } + + @Override + public Iterable searchObjects(ContainerId cid, ObjectFilter... filters) { + ClientWrapper client = connection(); + return client.getClient().searchObjects(cid, filters); + } + + @Override + public byte[] addChain(Chain chain, ChainTarget chainTarget) { + ClientWrapper client = connection(); + return client.getClient().addChain(chain, chainTarget); + } + + @Override + public void removeChain(Chain chain, ChainTarget chainTarget) { + ClientWrapper client = connection(); + client.getClient().removeChain(chain, chainTarget); + } + + @Override + public List listChains(ChainTarget chainTarget) { + ClientWrapper client = connection(); + return client.getClient().listChains(chainTarget); + } + + @Override + public NetmapSnapshot getNetmapSnapshot() { + ClientWrapper client = connection(); + return client.getClient().getNetmapSnapshot(); + } + + @Override + public NodeInfo getLocalNodeInfo() { + ClientWrapper client = connection(); + return client.getClient().getLocalNodeInfo(); + } + + @Override + public NetworkSettings getNetworkSettings() { + ClientWrapper client = connection(); + return client.getClient().getNetworkSettings(); + } + + @Override + public SessionToken createSession(long expiration) { + ClientWrapper client = connection(); + return client.getClient().createSession(expiration); + } + + public frostfs.session.Types.SessionToken createSessionInternal(long expiration) { + ClientWrapper client = connection(); + return client.getClient().createSessionInternal(expiration); + } + + @Override + public ObjectId calculateObjectId(ObjectHeader header) { + ClientWrapper client = connection(); + return client.getClient().calculateObjectId(header); + } + + @Override + public frostfs.accounting.Types.Decimal getBalance() { + ClientWrapper client = connection(); + return client.getClient().getBalance(); + } +} diff --git a/client/src/main/java/info/frostfs/sdk/pool/RebalanceParameters.java b/client/src/main/java/info/frostfs/sdk/pool/RebalanceParameters.java new file mode 100644 index 0000000..d7bd790 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/pool/RebalanceParameters.java @@ -0,0 +1,15 @@ +package info.frostfs.sdk.pool; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +public class RebalanceParameters { + private NodesParam[] nodesParams; + private long nodeRequestTimeout; + private long clientRebalanceInterval; + private long sessionExpirationDuration; +} diff --git a/client/src/main/java/info/frostfs/sdk/pool/RequestInfo.java b/client/src/main/java/info/frostfs/sdk/pool/RequestInfo.java new file mode 100644 index 0000000..68d8312 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/pool/RequestInfo.java @@ -0,0 +1,15 @@ +package info.frostfs.sdk.pool; + +import info.frostfs.sdk.enums.MethodIndex; +import lombok.Getter; +import lombok.Setter; + +import java.time.Duration; + +@Getter +@Setter +public class RequestInfo { + private String address; + private MethodIndex methodIndex; + private Duration elapsed; +} diff --git a/client/src/main/java/info/frostfs/sdk/pool/Sampler.java b/client/src/main/java/info/frostfs/sdk/pool/Sampler.java new file mode 100644 index 0000000..505243a --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/pool/Sampler.java @@ -0,0 +1,77 @@ +package info.frostfs.sdk.pool; + +import java.util.ArrayList; +import java.util.Random; + +class Sampler { + private final Object lock = new Object(); + private final Random random = new Random(); + private final double[] probabilities; + private final int[] alias; + + Sampler(double[] probabilities) { + ArrayList small = new ArrayList<>(); + ArrayList large = new ArrayList<>(); + + int n = probabilities.length; + + this.probabilities = new double[n]; + this.alias = new int[n]; + + // Compute scaled probabilities. + double[] p = new double[n]; + + for (int i = 0; i < n; i++) { + p[i] = probabilities[i] * n; + if (p[i] < 1) { + small.add(i); + } else { + large.add(i); + } + } + + while (!small.isEmpty() && !large.isEmpty()) { + int l = small.remove(small.size() - 1); + int g = large.remove(large.size() - 1); + + this.probabilities[l] = p[l]; + this.alias[l] = g; + + p[g] = p[g] + p[l] - 1; + + if (p[g] < 1) { + small.add(g); + } else { + large.add(g); + } + } + + while (!large.isEmpty()) { + int g = large.remove(large.size() - 1); + this.probabilities[g] = 1; + } + + while (!small.isEmpty()) { + int l = small.remove(small.size() - 1); + probabilities[l] = 1; + } + } + + int next() { + int n = alias.length; + + int i; + double f; + synchronized (lock) { + i = random.nextInt(n); + f = random.nextDouble(); + } + + if (f < probabilities[i]) { + return i; + } + + return alias[i]; + } +} + diff --git a/client/src/main/java/info/frostfs/sdk/pool/SessionCache.java b/client/src/main/java/info/frostfs/sdk/pool/SessionCache.java new file mode 100644 index 0000000..f64865b --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/pool/SessionCache.java @@ -0,0 +1,46 @@ +package info.frostfs.sdk.pool; + +import info.frostfs.sdk.dto.session.SessionToken; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +public class SessionCache { + private final ConcurrentMap cache = new ConcurrentHashMap<>(); + private final long tokenDuration; + private long currentEpoch; + + public SessionCache(long sessionExpirationDuration) { + this.tokenDuration = sessionExpirationDuration; + } + + public boolean contains(String key) { + return cache.containsKey(key); + } + + public boolean tryGetValue(String key, SessionToken[] value) { + if (key == null) { + value[0] = null; + return false; + } + + SessionToken token = cache.get(key); + if (token != null) { + value[0] = token; + return true; + } + return false; + } + + public void setValue(String key, SessionToken value) { + if (key != null) { + cache.put(key, value); + } + } + + public void deleteByPrefix(String prefix) { + cache.keySet().removeIf(key -> key.startsWith(prefix)); + } +} + + diff --git a/client/src/main/java/info/frostfs/sdk/pool/Statistic.java b/client/src/main/java/info/frostfs/sdk/pool/Statistic.java new file mode 100644 index 0000000..c7bc2ea --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/pool/Statistic.java @@ -0,0 +1,15 @@ +package info.frostfs.sdk.pool; + +import lombok.Getter; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.List; + +@Getter +@Setter +public class Statistic { + private long overallErrors; + private List nodes = new ArrayList<>(); + private String[] currentNodes; +} diff --git a/client/src/main/java/info/frostfs/sdk/pool/StatusSnapshot.java b/client/src/main/java/info/frostfs/sdk/pool/StatusSnapshot.java new file mode 100644 index 0000000..99a797b --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/pool/StatusSnapshot.java @@ -0,0 +1,13 @@ +package info.frostfs.sdk.pool; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class StatusSnapshot { + private long allTime; + private long allRequests; +} + + diff --git a/client/src/main/java/info/frostfs/sdk/pool/WorkList.java b/client/src/main/java/info/frostfs/sdk/pool/WorkList.java new file mode 100644 index 0000000..7e7a455 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/pool/WorkList.java @@ -0,0 +1,22 @@ +package info.frostfs.sdk.pool; + +import java.util.ArrayList; +import java.util.List; + +class WorkList { + private final List elements = new ArrayList<>(); + + private int getLength() { + return elements.size(); + } + + private void add(int element) { + elements.add(element); + } + + private int remove() { + int last = elements.get(elements.size() - 1); + elements.remove(elements.size() - 1); + return last; + } +} diff --git a/client/src/main/java/info/frostfs/sdk/pool/WrapperPrm.java b/client/src/main/java/info/frostfs/sdk/pool/WrapperPrm.java new file mode 100644 index 0000000..d03efc4 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/pool/WrapperPrm.java @@ -0,0 +1,26 @@ +package info.frostfs.sdk.pool; + +import info.frostfs.sdk.jdo.ECDsa; +import io.grpc.ClientInterceptors; +import io.grpc.ManagedChannelBuilder; +import lombok.Getter; +import lombok.Setter; +import org.slf4j.Logger; + +import java.util.Collection; + +@Getter +@Setter +public class WrapperPrm { + private Logger logger; + private String address; + private ECDsa key; + private long dialTimeout; + private long streamTimeout; + private int errorThreshold; + private Runnable responseInfoCallback; + private Runnable poolRequestInfoCallback; + private ManagedChannelBuilder grpcChannelOptions; + private long gracefulCloseOnSwitchTimeout; + private Collection interceptors; +} diff --git a/client/src/main/java/info/frostfs/sdk/services/AccountingClient.java b/client/src/main/java/info/frostfs/sdk/services/AccountingClient.java new file mode 100644 index 0000000..e29e153 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/services/AccountingClient.java @@ -0,0 +1,7 @@ +package info.frostfs.sdk.services; + +import frostfs.accounting.Types; + +public interface AccountingClient { + Types.Decimal getBalance(); +} diff --git a/client/src/main/java/info/frostfs/sdk/services/CommonClient.java b/client/src/main/java/info/frostfs/sdk/services/CommonClient.java new file mode 100644 index 0000000..e0482fb --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/services/CommonClient.java @@ -0,0 +1,5 @@ +package info.frostfs.sdk.services; + +public interface CommonClient extends + AccountingClient, ApeManagerClient, ContainerClient, NetmapClient, ObjectClient, SessionClient, ToolsClient { +} diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/AccountingClientImpl.java b/client/src/main/java/info/frostfs/sdk/services/impl/AccountingClientImpl.java new file mode 100644 index 0000000..deab5c2 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/services/impl/AccountingClientImpl.java @@ -0,0 +1,46 @@ +package info.frostfs.sdk.services.impl; + +import frostfs.accounting.AccountingServiceGrpc; +import frostfs.accounting.Service; +import frostfs.accounting.Types; +import info.frostfs.sdk.jdo.ClientEnvironment; +import info.frostfs.sdk.mappers.object.OwnerIdMapper; +import info.frostfs.sdk.services.AccountingClient; +import info.frostfs.sdk.services.ContextAccessor; +import info.frostfs.sdk.tools.RequestConstructor; +import info.frostfs.sdk.tools.RequestSigner; +import info.frostfs.sdk.tools.Verifier; + +import java.util.Map; + +public class AccountingClientImpl extends ContextAccessor implements AccountingClient { + private final AccountingServiceGrpc.AccountingServiceBlockingStub serviceBlockingStub; + + public AccountingClientImpl(ClientEnvironment clientEnvironment) { + super(clientEnvironment); + this.serviceBlockingStub = AccountingServiceGrpc.newBlockingStub(getContext().getChannel()); + } + + @Override + public Types.Decimal getBalance() { + var request = createGetRequest(null); + + var response = serviceBlockingStub.balance(request); + + Verifier.checkResponse(response); + return response.getBody().getBalance(); + } + + private Service.BalanceRequest createGetRequest(Map xHeaders) { + var body = Service.BalanceRequest.Body.newBuilder() + .setOwnerId(OwnerIdMapper.toGrpcMessage(getContext().getOwnerId())) + .build(); + var request = Service.BalanceRequest.newBuilder() + .setBody(body); + + RequestConstructor.addMetaHeader(request, xHeaders); + RequestSigner.sign(request, getContext().getKey()); + + return request.build(); + } +} diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/ObjectToolsImpl.java b/client/src/main/java/info/frostfs/sdk/services/impl/ObjectToolsImpl.java index 9730f34..abe6927 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/ObjectToolsImpl.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/ObjectToolsImpl.java @@ -47,16 +47,16 @@ public class ObjectToolsImpl extends ContextAccessor implements ToolsClient { ); } - public Types.Object createObject(ObjectFrostFS objectFrostFs) { - objectFrostFs.getHeader().setOwnerId(getContext().getOwnerId()); - objectFrostFs.getHeader().setVersion(getContext().getVersion()); - objectFrostFs.getHeader().setPayloadLength(objectFrostFs.getPayload().length); + public Types.Object createObject(ObjectFrostFS objectFrostFS) { + objectFrostFS.getHeader().setOwnerId(getContext().getOwnerId()); + objectFrostFS.getHeader().setVersion(getContext().getVersion()); + objectFrostFS.getHeader().setPayloadLength(objectFrostFS.getPayload().length); - var grpcHeader = ObjectHeaderMapper.toGrpcMessage(objectFrostFs.getHeader()).toBuilder() - .setPayloadHash(sha256Checksum(objectFrostFs.getPayload())) + var grpcHeader = ObjectHeaderMapper.toGrpcMessage(objectFrostFS.getHeader()).toBuilder() + .setPayloadHash(sha256Checksum(objectFrostFS.getPayload())) .build(); - var split = objectFrostFs.getHeader().getSplit(); + var split = objectFrostFS.getHeader().getSplit(); if (nonNull(split)) { grpcHeader = updateSplitValues(grpcHeader, split); } @@ -68,7 +68,7 @@ public class ObjectToolsImpl extends ContextAccessor implements ToolsClient { return Types.Object.newBuilder() .setHeader(grpcHeader) .setObjectId(objectId) - .setPayload(ByteString.copyFrom(objectFrostFs.getPayload())) + .setPayload(ByteString.copyFrom(objectFrostFS.getPayload())) .setSignature(sig) .build(); } diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/ClientMetrics.java b/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/ClientMetrics.java index 149c145..656d19f 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/ClientMetrics.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/ClientMetrics.java @@ -15,10 +15,10 @@ import static info.frostfs.sdk.services.impl.interceptor.Labels.*; public class ClientMetrics { private static final List defaultRequestLabels = - Arrays.asList("grpc_type", "grpc_service", "grpc_method"); + Arrays.asList("grpc_target", "grpc_type", "grpc_service", "grpc_method"); private static final List defaultResponseLabels = - Arrays.asList("grpc_type", "grpc_service", "grpc_method", "code", "grpc_code"); + Arrays.asList("grpc_target", "grpc_type", "grpc_service", "grpc_method", "code", "grpc_code"); private static final Counter.Builder rpcStartedBuilder = Counter.build() @@ -113,7 +113,9 @@ public class ClientMetrics { .observe(latencySec); } - /** Knows how to produce {@link ClientMetrics} instances for individual methods. */ + /** + * Knows how to produce {@link ClientMetrics} instances for individual methods. + */ static class Factory { private final List> labelHeaderKeys; private final Counter rpcStarted; @@ -155,7 +157,9 @@ public class ClientMetrics { } } - /** Creates a {@link ClientMetrics} for the supplied gRPC method. */ + /** + * Creates a {@link ClientMetrics} for the supplied gRPC method. + */ ClientMetrics createMetricsForMethod(GrpcMethod grpcMethod) { return new ClientMetrics( labelHeaderKeys, diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/GrpcMethod.java b/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/GrpcMethod.java index 8f9aa4c..41e4ef1 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/GrpcMethod.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/GrpcMethod.java @@ -1,27 +1,33 @@ package info.frostfs.sdk.services.impl.interceptor; +import io.grpc.Channel; import io.grpc.MethodDescriptor; import io.grpc.MethodDescriptor.MethodType; public class GrpcMethod { + private final String targetName; private final String serviceName; private final String methodName; private final MethodType type; - private GrpcMethod(String serviceName, String methodName, MethodType type) { + private GrpcMethod(String targetName, String serviceName, String methodName, MethodType type) { + this.targetName = targetName; this.serviceName = serviceName; this.methodName = methodName; this.type = type; } - static GrpcMethod of(MethodDescriptor method) { + static GrpcMethod of(MethodDescriptor method, Channel channel) { String serviceName = MethodDescriptor.extractFullServiceName(method.getFullMethodName()); // Full method names are of the form: "full.serviceName/MethodName". We extract the last part. String methodName = method.getFullMethodName().substring(serviceName.length() + 1); - return new GrpcMethod(serviceName, methodName, method.getType()); + return new GrpcMethod(channel.authority(), serviceName, methodName, method.getType()); } + String targetName() { + return targetName; + } String serviceName() { return serviceName; diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/Labels.java b/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/Labels.java index ba44eaf..e728a79 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/Labels.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/Labels.java @@ -53,6 +53,7 @@ public class Labels { */ static T addLabels(SimpleCollector collector, List labels, GrpcMethod method) { List allLabels = new ArrayList<>(); + allLabels.add(method.targetName()); allLabels.add(method.type()); allLabels.add(method.serviceName()); allLabels.add(method.methodName()); diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/MonitoringClientInterceptor.java b/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/MonitoringClientInterceptor.java index ada4761..4654487 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/MonitoringClientInterceptor.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/MonitoringClientInterceptor.java @@ -1,11 +1,8 @@ package info.frostfs.sdk.services.impl.interceptor; +import io.grpc.*; + import java.time.Clock; -import io.grpc.CallOptions; -import io.grpc.Channel; -import io.grpc.ClientCall; -import io.grpc.ClientInterceptor; -import io.grpc.MethodDescriptor; public class MonitoringClientInterceptor implements ClientInterceptor { @@ -19,6 +16,7 @@ public class MonitoringClientInterceptor implements ClientInterceptor { this.configuration = configuration; this.clientMetricsFactory = clientMetricsFactory; } + public static MonitoringClientInterceptor create(Configuration configuration) { return new MonitoringClientInterceptor( Clock.systemDefaultZone(), configuration, new ClientMetrics.Factory(configuration)); @@ -27,7 +25,7 @@ public class MonitoringClientInterceptor implements ClientInterceptor { @Override public ClientCall interceptCall( MethodDescriptor methodDescriptor, CallOptions callOptions, Channel channel) { - GrpcMethod grpcMethod = GrpcMethod.of(methodDescriptor); + GrpcMethod grpcMethod = GrpcMethod.of(methodDescriptor, channel); ClientMetrics metrics = clientMetricsFactory.createMetricsForMethod(grpcMethod); return new MonitoringClientCall<>( channel.newCall(methodDescriptor, callOptions), metrics, grpcMethod, configuration, clock); diff --git a/client/src/main/java/info/frostfs/sdk/tools/GrpcClient.java b/client/src/main/java/info/frostfs/sdk/tools/GrpcClient.java index 13cdfe5..9692e3a 100644 --- a/client/src/main/java/info/frostfs/sdk/tools/GrpcClient.java +++ b/client/src/main/java/info/frostfs/sdk/tools/GrpcClient.java @@ -3,7 +3,7 @@ package info.frostfs.sdk.tools; import info.frostfs.sdk.exceptions.ValidationFrostFSException; import info.frostfs.sdk.jdo.ClientSettings; import info.frostfs.sdk.utils.Validator; -import io.grpc.Channel; +import io.grpc.ManagedChannel; import io.grpc.netty.NettyChannelBuilder; import java.net.URI; @@ -16,7 +16,7 @@ public class GrpcClient { private GrpcClient() { } - public static Channel initGrpcChannel(ClientSettings clientSettings) { + public static ManagedChannel initGrpcChannel(ClientSettings clientSettings) { Validator.validate(clientSettings); try { @@ -32,4 +32,15 @@ public class GrpcClient { ); } } + + public static ManagedChannel initGrpcChannel(String address) { + try { + URI uri = new URI(address); + return NettyChannelBuilder.forAddress(uri.getHost(), uri.getPort()).usePlaintext().build(); + } catch (URISyntaxException exp) { + throw new ValidationFrostFSException( + String.format(INVALID_HOST_TEMPLATE, address, exp.getMessage()) + ); + } + } } diff --git a/client/src/main/java/info/frostfs/sdk/utils/FrostFSMessages.java b/client/src/main/java/info/frostfs/sdk/utils/FrostFSMessages.java new file mode 100644 index 0000000..04b7dab --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/utils/FrostFSMessages.java @@ -0,0 +1,20 @@ +package info.frostfs.sdk.utils; + +import org.slf4j.Logger; + +public class FrostFSMessages { + private FrostFSMessages() { + } + + public static void sessionCreationError(Logger logger, String address, String error) { + logger.warn("Failed to create frostfs session token for client. Address {}, {}", address, error); + } + + public static void errorThresholdReached(Logger logger, String address, long threshold) { + logger.warn("Error threshold reached. Address {}, threshold {}", address, threshold); + } + + public static void healthChanged(Logger logger, String address, boolean healthy, String error) { + logger.warn("Health has changed: {} healthy {}, reason {}", address, healthy, error); + } +} diff --git a/client/src/test/java/info/frostfs/sdk/services/AccountingClientTest.java b/client/src/test/java/info/frostfs/sdk/services/AccountingClientTest.java new file mode 100644 index 0000000..e887223 --- /dev/null +++ b/client/src/test/java/info/frostfs/sdk/services/AccountingClientTest.java @@ -0,0 +1,102 @@ +package info.frostfs.sdk.services; + +import frostfs.accounting.AccountingServiceGrpc; +import frostfs.accounting.Service; +import info.frostfs.sdk.Base58; +import info.frostfs.sdk.dto.object.OwnerId; +import info.frostfs.sdk.jdo.ClientEnvironment; +import info.frostfs.sdk.services.impl.AccountingClientImpl; +import info.frostfs.sdk.testgenerator.AccountingGenerator; +import info.frostfs.sdk.tools.RequestConstructor; +import info.frostfs.sdk.tools.RequestSigner; +import info.frostfs.sdk.tools.Verifier; +import io.grpc.Channel; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.platform.commons.util.ReflectionUtils; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.lang.reflect.Field; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class AccountingClientTest { + private static final String OWNER_ID = "NVxUSpEEJzYXZZtUs18PrJTD9QZkLLNQ8S"; + + private AccountingClientImpl accountingClient; + + @Mock + private AccountingServiceGrpc.AccountingServiceBlockingStub AccountingServiceClient; + @Mock + private ClientEnvironment clientEnvironment; + @Mock + private Channel channel; + + private MockedStatic verifierMock; + private MockedStatic requestConstructorMock; + private MockedStatic requestSignerMock; + + @BeforeEach + void setUp() throws IllegalAccessException { + when(clientEnvironment.getChannel()).thenReturn(channel); + when(clientEnvironment.getOwnerId()).thenReturn(new OwnerId(OWNER_ID)); + + accountingClient = new AccountingClientImpl(clientEnvironment); + + Field field = ReflectionUtils.findFields(AccountingClientImpl.class, + f -> f.getName().equals("serviceBlockingStub"), + ReflectionUtils.HierarchyTraversalMode.TOP_DOWN) + .get(0); + + field.setAccessible(true); + field.set(accountingClient, AccountingServiceClient); + + verifierMock = Mockito.mockStatic(Verifier.class); + requestConstructorMock = Mockito.mockStatic(RequestConstructor.class); + requestSignerMock = Mockito.mockStatic(RequestSigner.class); + } + + @AfterEach + void cleanUp() { + verifierMock.close(); + requestConstructorMock.close(); + requestSignerMock.close(); + } + + @Test + void getBalance_success() { + //Given + var response = AccountingGenerator.generateBalanceResponse(); + + var captor = ArgumentCaptor.forClass(Service.BalanceRequest.class); + + when(AccountingServiceClient.balance(captor.capture())).thenReturn(response); + + //When + var result = accountingClient.getBalance(); + + //Then + requestConstructorMock.verify( + () -> RequestConstructor.addMetaHeader(any(Service.BalanceRequest.Builder.class), eq(null)), + times(1) + ); + requestSignerMock.verify( + () -> RequestSigner.sign(any(Service.BalanceRequest.Builder.class), eq(null)), + times(1) + ); + verifierMock.verify(() -> Verifier.checkResponse(response), times(1)); + + assertEquals(response.getBody().getBalance(), result); + + var request = captor.getValue(); + assertEquals(OWNER_ID, Base58.encode(request.getBody().getOwnerId().getValue().toByteArray())); + } +} diff --git a/client/src/test/java/info/frostfs/sdk/testgenerator/AccountingGenerator.java b/client/src/test/java/info/frostfs/sdk/testgenerator/AccountingGenerator.java new file mode 100644 index 0000000..13a59d2 --- /dev/null +++ b/client/src/test/java/info/frostfs/sdk/testgenerator/AccountingGenerator.java @@ -0,0 +1,42 @@ +package info.frostfs.sdk.testgenerator; + +import frostfs.accounting.Service; +import frostfs.accounting.Types; + +public class AccountingGenerator { + + public static Service.BalanceRequest generateBalanceRequest() { + return Service.BalanceRequest.newBuilder() + .setBody(generateBalanceRequestBody()) + .setMetaHeader(SessionGenerator.generateRequestMetaHeader()) + .setVerifyHeader(SessionGenerator.generateRequestVerificationHeader()) + .build(); + } + + public static Service.BalanceRequest.Body generateBalanceRequestBody() { + return Service.BalanceRequest.Body.newBuilder() + .setOwnerId(RefsGenerator.generateOwnerID()) + .build(); + } + + public static Service.BalanceResponse generateBalanceResponse() { + return Service.BalanceResponse.newBuilder() + .setBody(generateBalanceResponseBody()) + .setMetaHeader(SessionGenerator.generateResponseMetaHeader()) + .setVerifyHeader(SessionGenerator.generateResponseVerificationHeader()) + .build(); + } + + public static Service.BalanceResponse.Body generateBalanceResponseBody() { + return Service.BalanceResponse.Body.newBuilder() + .setBalance(generateDecimal()) + .build(); + } + + public static Types.Decimal generateDecimal() { + return Types.Decimal.newBuilder() + .setValue(1) + .setPrecision(2) + .build(); + } +} diff --git a/cryptography/pom.xml b/cryptography/pom.xml index 33bf8fb..606a193 100644 --- a/cryptography/pom.xml +++ b/cryptography/pom.xml @@ -6,7 +6,7 @@ info.frostfs.sdk frostfs-sdk-java - 0.1.0 + 0.2.0 cryptography @@ -21,7 +21,7 @@ info.frostfs.sdk exceptions - 0.1.0 + 0.2.0 com.google.protobuf diff --git a/exceptions/pom.xml b/exceptions/pom.xml index 2c80de2..a24f1fc 100644 --- a/exceptions/pom.xml +++ b/exceptions/pom.xml @@ -6,7 +6,7 @@ info.frostfs.sdk frostfs-sdk-java - 0.1.0 + 0.2.0 exceptions diff --git a/exceptions/src/main/java/info/frostfs/sdk/constants/ErrorConst.java b/exceptions/src/main/java/info/frostfs/sdk/constants/ErrorConst.java index 1bf25d4..26f3eac 100644 --- a/exceptions/src/main/java/info/frostfs/sdk/constants/ErrorConst.java +++ b/exceptions/src/main/java/info/frostfs/sdk/constants/ErrorConst.java @@ -40,6 +40,11 @@ public class ErrorConst { public static final String WRONG_UUID_SIZE_TEMPLATE = "uuid byte array length must be %s"; + public static final String POOL_CLIENT_UNHEALTHY = "pool client unhealthy"; + public static final String POOL_PEERS_IS_MISSING = "no FrostFS peers configured"; + public static final String POOL_NODES_UNHEALTHY = "at least one node must be healthy"; + public static final String POOL_CLIENTS_UNHEALTHY = "cannot find alive client"; + public static final String POOL_NOT_DIALED = "pool not dialed"; public static final String FIELDS_DELIMITER_COMMA = ", "; public static final String FIELDS_DELIMITER_OR = " or "; diff --git a/exceptions/src/main/java/info/frostfs/sdk/exceptions/FrostFSException.java b/exceptions/src/main/java/info/frostfs/sdk/exceptions/FrostFSException.java new file mode 100644 index 0000000..fc41bb9 --- /dev/null +++ b/exceptions/src/main/java/info/frostfs/sdk/exceptions/FrostFSException.java @@ -0,0 +1,15 @@ +package info.frostfs.sdk.exceptions; + +public class FrostFSException extends RuntimeException { + + public FrostFSException() { + } + + public FrostFSException(String message) { + super(message); + } + + public FrostFSException(Throwable cause) { + super(cause); + } +} diff --git a/exceptions/src/main/java/info/frostfs/sdk/exceptions/ProcessFrostFSException.java b/exceptions/src/main/java/info/frostfs/sdk/exceptions/ProcessFrostFSException.java index 47e06b3..6cd3560 100644 --- a/exceptions/src/main/java/info/frostfs/sdk/exceptions/ProcessFrostFSException.java +++ b/exceptions/src/main/java/info/frostfs/sdk/exceptions/ProcessFrostFSException.java @@ -1,6 +1,6 @@ package info.frostfs.sdk.exceptions; -public class ProcessFrostFSException extends RuntimeException { +public class ProcessFrostFSException extends FrostFSException { public ProcessFrostFSException(String message) { super(message); } diff --git a/exceptions/src/main/java/info/frostfs/sdk/exceptions/SessionExpiredFrostFSException.java b/exceptions/src/main/java/info/frostfs/sdk/exceptions/SessionExpiredFrostFSException.java new file mode 100644 index 0000000..1a3a1e8 --- /dev/null +++ b/exceptions/src/main/java/info/frostfs/sdk/exceptions/SessionExpiredFrostFSException.java @@ -0,0 +1,8 @@ +package info.frostfs.sdk.exceptions; + +public class SessionExpiredFrostFSException extends FrostFSException { + + public SessionExpiredFrostFSException(String message) { + super(message); + } +} diff --git a/exceptions/src/main/java/info/frostfs/sdk/exceptions/SessionNotFoundFrostFSException.java b/exceptions/src/main/java/info/frostfs/sdk/exceptions/SessionNotFoundFrostFSException.java new file mode 100644 index 0000000..0e8522b --- /dev/null +++ b/exceptions/src/main/java/info/frostfs/sdk/exceptions/SessionNotFoundFrostFSException.java @@ -0,0 +1,8 @@ +package info.frostfs.sdk.exceptions; + +public class SessionNotFoundFrostFSException extends FrostFSException { + + public SessionNotFoundFrostFSException(String message) { + super(message); + } +} diff --git a/exceptions/src/main/java/info/frostfs/sdk/exceptions/TimeoutFrostFSException.java b/exceptions/src/main/java/info/frostfs/sdk/exceptions/TimeoutFrostFSException.java index fca9263..5e1ee20 100644 --- a/exceptions/src/main/java/info/frostfs/sdk/exceptions/TimeoutFrostFSException.java +++ b/exceptions/src/main/java/info/frostfs/sdk/exceptions/TimeoutFrostFSException.java @@ -1,6 +1,6 @@ package info.frostfs.sdk.exceptions; -public class TimeoutFrostFSException extends RuntimeException { +public class TimeoutFrostFSException extends FrostFSException { public TimeoutFrostFSException() { } } diff --git a/exceptions/src/main/java/info/frostfs/sdk/exceptions/ValidationFrostFSException.java b/exceptions/src/main/java/info/frostfs/sdk/exceptions/ValidationFrostFSException.java index d294ef8..74e5259 100644 --- a/exceptions/src/main/java/info/frostfs/sdk/exceptions/ValidationFrostFSException.java +++ b/exceptions/src/main/java/info/frostfs/sdk/exceptions/ValidationFrostFSException.java @@ -1,6 +1,6 @@ package info.frostfs.sdk.exceptions; -public class ValidationFrostFSException extends RuntimeException { +public class ValidationFrostFSException extends FrostFSException { public ValidationFrostFSException(String message) { super(message); } diff --git a/models/pom.xml b/models/pom.xml index a5dfe24..08dbdc6 100644 --- a/models/pom.xml +++ b/models/pom.xml @@ -6,7 +6,7 @@ info.frostfs.sdk frostfs-sdk-java - 0.1.0 + 0.2.0 models @@ -21,17 +21,17 @@ info.frostfs.sdk cryptography - 0.1.0 + 0.2.0 info.frostfs.sdk protos - 0.1.0 + 0.2.0 info.frostfs.sdk exceptions - 0.1.0 + 0.2.0 diff --git a/models/src/main/java/info/frostfs/sdk/constants/AppConst.java b/models/src/main/java/info/frostfs/sdk/constants/AppConst.java index 9942aa7..67a2422 100644 --- a/models/src/main/java/info/frostfs/sdk/constants/AppConst.java +++ b/models/src/main/java/info/frostfs/sdk/constants/AppConst.java @@ -1,6 +1,8 @@ package info.frostfs.sdk.constants; public class AppConst { + public static final String RESERVED_PREFIX = "__SYSTEM__"; + public static final int DEFAULT_MAJOR_VERSION = 2; public static final int DEFAULT_MINOR_VERSION = 13; public static final int BYTE_SHIFT = 10; diff --git a/models/src/main/java/info/frostfs/sdk/constants/AttributeConst.java b/models/src/main/java/info/frostfs/sdk/constants/AttributeConst.java new file mode 100644 index 0000000..214a9e3 --- /dev/null +++ b/models/src/main/java/info/frostfs/sdk/constants/AttributeConst.java @@ -0,0 +1,10 @@ +package info.frostfs.sdk.constants; + +import static info.frostfs.sdk.constants.AppConst.RESERVED_PREFIX; + +public class AttributeConst { + public static final String DISABLE_HOMOMORPHIC_HASHING_ATTRIBUTE = RESERVED_PREFIX + "DISABLE_HOMOMORPHIC_HASHING"; + + private AttributeConst() { + } +} diff --git a/models/src/main/java/info/frostfs/sdk/constants/XHeaderConst.java b/models/src/main/java/info/frostfs/sdk/constants/XHeaderConst.java index e8b2c72..805762a 100644 --- a/models/src/main/java/info/frostfs/sdk/constants/XHeaderConst.java +++ b/models/src/main/java/info/frostfs/sdk/constants/XHeaderConst.java @@ -1,9 +1,10 @@ package info.frostfs.sdk.constants; +import static info.frostfs.sdk.constants.AppConst.RESERVED_PREFIX; + public class XHeaderConst { - public static final String RESERVED_XHEADER_PREFIX = "__SYSTEM__"; - public static final String XHEADER_NETMAP_EPOCH = RESERVED_XHEADER_PREFIX + "NETMAP_EPOCH"; - public static final String XHEADER_NETMAP_LOOKUP_DEPTH = RESERVED_XHEADER_PREFIX + "NETMAP_LOOKUP_DEPTH"; + public static final String XHEADER_NETMAP_EPOCH = RESERVED_PREFIX + "NETMAP_EPOCH"; + public static final String XHEADER_NETMAP_LOOKUP_DEPTH = RESERVED_PREFIX + "NETMAP_LOOKUP_DEPTH"; private XHeaderConst() { } diff --git a/models/src/main/java/info/frostfs/sdk/dto/container/Container.java b/models/src/main/java/info/frostfs/sdk/dto/container/Container.java index 2d0816e..6f04b2e 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/container/Container.java +++ b/models/src/main/java/info/frostfs/sdk/dto/container/Container.java @@ -6,6 +6,8 @@ import info.frostfs.sdk.enums.BasicAcl; import lombok.Getter; import lombok.Setter; +import java.util.HashMap; +import java.util.Map; import java.util.UUID; @Getter @@ -15,6 +17,7 @@ public class Container { private BasicAcl basicAcl; private PlacementPolicy placementPolicy; private Version version; + private Map attributes = new HashMap<>(); public Container(BasicAcl basicAcl, PlacementPolicy placementPolicy) { this.nonce = UUID.randomUUID(); diff --git a/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerMapper.java index 76f8479..9b9dd72 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerMapper.java @@ -7,9 +7,13 @@ import info.frostfs.sdk.enums.BasicAcl; import info.frostfs.sdk.exceptions.ProcessFrostFSException; import info.frostfs.sdk.mappers.netmap.PlacementPolicyMapper; import info.frostfs.sdk.mappers.netmap.VersionMapper; +import org.apache.commons.collections4.CollectionUtils; + +import java.util.stream.Collectors; import static info.frostfs.sdk.UuidExtension.asBytes; import static info.frostfs.sdk.UuidExtension.asUuid; +import static info.frostfs.sdk.constants.AttributeConst.DISABLE_HOMOMORPHIC_HASHING_ATTRIBUTE; import static info.frostfs.sdk.constants.ErrorConst.UNKNOWN_ENUM_VALUE_TEMPLATE; import static java.util.Objects.isNull; @@ -22,11 +26,23 @@ public class ContainerMapper { return null; } - return Types.Container.newBuilder() + var containerGrpc = Types.Container.newBuilder() .setBasicAcl(container.getBasicAcl().value) .setPlacementPolicy(PlacementPolicyMapper.toGrpcMessage(container.getPlacementPolicy())) - .setNonce(ByteString.copyFrom(asBytes(container.getNonce()))) - .build(); + .setNonce(ByteString.copyFrom(asBytes(container.getNonce()))); + + container.getAttributes().putIfAbsent(DISABLE_HOMOMORPHIC_HASHING_ATTRIBUTE, Boolean.TRUE.toString()); + var attributes = container.getAttributes().entrySet().stream() + .map(entry -> + Types.Container.Attribute.newBuilder() + .setKey(entry.getKey()) + .setValue(entry.getValue()) + .build() + ) + .collect(Collectors.toList()); + containerGrpc.addAllAttributes(attributes); + + return containerGrpc.build(); } public static Container toModel(Types.Container containerGrpc) { @@ -41,9 +57,17 @@ public class ContainerMapper { ); } + var container = new Container(basicAcl, PlacementPolicyMapper.toModel(containerGrpc.getPlacementPolicy())); container.setNonce(asUuid(containerGrpc.getNonce().toByteArray())); container.setVersion(VersionMapper.toModel(containerGrpc.getVersion())); + + if (CollectionUtils.isNotEmpty(containerGrpc.getAttributesList())) { + var attributes = containerGrpc.getAttributesList().stream() + .collect(Collectors.toMap(Types.Container.Attribute::getKey, Types.Container.Attribute::getValue)); + container.setAttributes(attributes); + } + return container; } } diff --git a/models/src/test/java/info/frostfs/sdk/mappers/container/ContainerMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/container/ContainerMapperTest.java index d4c6dd4..4631766 100644 --- a/models/src/test/java/info/frostfs/sdk/mappers/container/ContainerMapperTest.java +++ b/models/src/test/java/info/frostfs/sdk/mappers/container/ContainerMapperTest.java @@ -15,6 +15,7 @@ import java.util.UUID; import static info.frostfs.sdk.UuidExtension.asBytes; import static info.frostfs.sdk.UuidExtension.asUuid; +import static info.frostfs.sdk.constants.AttributeConst.DISABLE_HOMOMORPHIC_HASHING_ATTRIBUTE; import static org.junit.jupiter.api.Assertions.*; public class ContainerMapperTest { @@ -24,6 +25,8 @@ public class ContainerMapperTest { //Given var placementPolicy = new PlacementPolicy(new Replica[]{new Replica(1)}, true); var container = new Container(BasicAcl.PUBLIC_RW, placementPolicy); + container.getAttributes().put("key1", "val1"); + container.getAttributes().put(DISABLE_HOMOMORPHIC_HASHING_ATTRIBUTE, "false"); //When var result = ContainerMapper.toGrpcMessage(container); @@ -42,6 +45,11 @@ public class ContainerMapperTest { container.getPlacementPolicy().getReplicas()[0].getSelector(), result.getPlacementPolicy().getReplicasList().get(0).getSelector() ); + + assertEquals("key1", result.getAttributes(0).getKey()); + assertEquals("val1", result.getAttributes(0).getValue()); + assertEquals(DISABLE_HOMOMORPHIC_HASHING_ATTRIBUTE, result.getAttributes(1).getKey()); + assertEquals("false", result.getAttributes(1).getValue()); } @Test @@ -69,11 +77,23 @@ public class ContainerMapperTest { .addReplicas(replica) .build(); + var attribute1 = Types.Container.Attribute.newBuilder() + .setKey("key1") + .setValue("val1") + .build(); + + var attribute2 = Types.Container.Attribute.newBuilder() + .setKey("key2") + .setValue("val2") + .build(); + var container = Types.Container.newBuilder() .setBasicAcl(basicAcl.value) .setNonce(ByteString.copyFrom(asBytes(UUID.randomUUID()))) .setVersion(version) .setPlacementPolicy(placementPolicy) + .addAttributes(attribute1) + .addAttributes(attribute2) .build(); //When @@ -95,6 +115,9 @@ public class ContainerMapperTest { ); assertEquals(version.getMajor(), result.getVersion().getMajor()); assertEquals(version.getMinor(), result.getVersion().getMinor()); + + assertEquals(attribute1.getValue(), result.getAttributes().get(attribute1.getKey())); + assertEquals(attribute2.getValue(), result.getAttributes().get(attribute2.getKey())); } @Test diff --git a/pom.xml b/pom.xml index bd46c8b..2c9f30e 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ info.frostfs.sdk frostfs-sdk-java - 0.1.0 + 0.2.0 pom client diff --git a/protos/pom.xml b/protos/pom.xml index 5381838..d08ae98 100644 --- a/protos/pom.xml +++ b/protos/pom.xml @@ -6,7 +6,7 @@ info.frostfs.sdk frostfs-sdk-java - 0.1.0 + 0.2.0 protos From 1ccb1f2013fed7de92a2437cba968eaa36d5716b Mon Sep 17 00:00:00 2001 From: Ori Bruk Date: Thu, 23 Jan 2025 15:30:38 +0300 Subject: [PATCH 28/38] [#34] Provide input parameters for all grpc methods Signed-off-by: Ori Bruk --- README.md | 112 ++++-- client/pom.xml | 8 +- .../java/info/frostfs/sdk/FrostFSClient.java | 110 +++--- .../frostfs/sdk/jdo/ClientEnvironment.java | 30 +- .../frostfs/sdk/jdo/PutObjectParameters.java | 40 -- .../sdk/jdo/parameters/CallContext.java | 23 ++ .../PrmWait.java} | 6 +- .../jdo/parameters/ape/PrmApeChainAdd.java | 27 ++ .../jdo/parameters/ape/PrmApeChainList.java | 23 ++ .../jdo/parameters/ape/PrmApeChainRemove.java | 27 ++ .../container/PrmContainerCreate.java | 33 ++ .../container/PrmContainerDelete.java | 33 ++ .../parameters/container/PrmContainerGet.java | 23 ++ .../container/PrmContainerGetAll.java | 16 + .../object/PrmObjectClientCutPut.java | 28 ++ .../parameters/object/PrmObjectDelete.java | 30 ++ .../jdo/parameters/object/PrmObjectGet.java | 30 ++ .../parameters/object/PrmObjectHeadGet.java | 31 ++ .../jdo/parameters/object/PrmObjectPut.java | 28 ++ .../parameters/object/PrmObjectPutBase.java | 12 + .../parameters/object/PrmObjectSearch.java | 30 ++ .../parameters/object/PrmObjectSinglePut.java | 26 ++ .../parameters/object/PutObjectContext.java | 14 + .../parameters/session/PrmSessionCreate.java | 19 + .../parameters/session/SessionContext.java | 7 + .../sdk/jdo/result/ObjectHeaderResult.java | 15 + .../info/frostfs/sdk/pool/ClientWrapper.java | 17 +- .../main/java/info/frostfs/sdk/pool/Pool.java | 129 +++--- .../info/frostfs/sdk/pool/SessionCache.java | 15 +- .../sdk/services/AccountingClient.java | 3 +- .../sdk/services/ApeManagerClient.java | 11 +- .../frostfs/sdk/services/ContainerClient.java | 13 +- .../frostfs/sdk/services/NetmapClient.java | 7 +- .../frostfs/sdk/services/ObjectClient.java | 22 +- .../frostfs/sdk/services/SessionClient.java | 4 +- .../frostfs/sdk/services/SessionTools.java | 4 +- .../services/impl/AccountingClientImpl.java | 14 +- .../services/impl/ApeManagerClientImpl.java | 81 ++-- .../services/impl/ContainerClientImpl.java | 119 +++--- .../sdk/services/impl/NetmapClientImpl.java | 22 +- .../sdk/services/impl/ObjectClientImpl.java | 370 +++++++++--------- .../sdk/services/impl/SessionClientImpl.java | 18 +- .../sdk/services/impl/SessionToolsImpl.java | 21 +- .../impl/interceptor/Configuration.java | 29 +- .../impl/rwhelper/ObjectStreamer.java | 62 +++ .../services/impl/rwhelper/ObjectWriter.java | 76 ++-- .../frostfs/sdk/tools/RequestConstructor.java | 22 +- .../info/frostfs/sdk/utils/DeadLineUtil.java | 25 ++ .../sdk/services/AccountingClientTest.java | 5 +- .../sdk/services/ApeManagerClientTest.java | 35 +- cryptography/pom.xml | 4 +- exceptions/pom.xml | 2 +- .../frostfs/sdk/constants/ErrorConst.java | 2 + models/pom.xml | 8 +- .../info/frostfs/sdk/constants/AppConst.java | 1 + .../frostfs/sdk/dto/object/SplitInfo.java | 17 + .../mappers/container/ContainerMapper.java | 2 - .../sdk/mappers/object/SplitInfoMapper.java | 30 ++ .../mappers/object/SplitInfoMapperTest.java | 61 +++ pom.xml | 2 +- protos/pom.xml | 2 +- 61 files changed, 1411 insertions(+), 625 deletions(-) delete mode 100644 client/src/main/java/info/frostfs/sdk/jdo/PutObjectParameters.java create mode 100644 client/src/main/java/info/frostfs/sdk/jdo/parameters/CallContext.java rename client/src/main/java/info/frostfs/sdk/jdo/{WaitParameters.java => parameters/PrmWait.java} (86%) create mode 100644 client/src/main/java/info/frostfs/sdk/jdo/parameters/ape/PrmApeChainAdd.java create mode 100644 client/src/main/java/info/frostfs/sdk/jdo/parameters/ape/PrmApeChainList.java create mode 100644 client/src/main/java/info/frostfs/sdk/jdo/parameters/ape/PrmApeChainRemove.java create mode 100644 client/src/main/java/info/frostfs/sdk/jdo/parameters/container/PrmContainerCreate.java create mode 100644 client/src/main/java/info/frostfs/sdk/jdo/parameters/container/PrmContainerDelete.java create mode 100644 client/src/main/java/info/frostfs/sdk/jdo/parameters/container/PrmContainerGet.java create mode 100644 client/src/main/java/info/frostfs/sdk/jdo/parameters/container/PrmContainerGetAll.java create mode 100644 client/src/main/java/info/frostfs/sdk/jdo/parameters/object/PrmObjectClientCutPut.java create mode 100644 client/src/main/java/info/frostfs/sdk/jdo/parameters/object/PrmObjectDelete.java create mode 100644 client/src/main/java/info/frostfs/sdk/jdo/parameters/object/PrmObjectGet.java create mode 100644 client/src/main/java/info/frostfs/sdk/jdo/parameters/object/PrmObjectHeadGet.java create mode 100644 client/src/main/java/info/frostfs/sdk/jdo/parameters/object/PrmObjectPut.java create mode 100644 client/src/main/java/info/frostfs/sdk/jdo/parameters/object/PrmObjectPutBase.java create mode 100644 client/src/main/java/info/frostfs/sdk/jdo/parameters/object/PrmObjectSearch.java create mode 100644 client/src/main/java/info/frostfs/sdk/jdo/parameters/object/PrmObjectSinglePut.java create mode 100644 client/src/main/java/info/frostfs/sdk/jdo/parameters/object/PutObjectContext.java create mode 100644 client/src/main/java/info/frostfs/sdk/jdo/parameters/session/PrmSessionCreate.java create mode 100644 client/src/main/java/info/frostfs/sdk/jdo/parameters/session/SessionContext.java create mode 100644 client/src/main/java/info/frostfs/sdk/jdo/result/ObjectHeaderResult.java create mode 100644 client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectStreamer.java create mode 100644 client/src/main/java/info/frostfs/sdk/utils/DeadLineUtil.java create mode 100644 models/src/main/java/info/frostfs/sdk/dto/object/SplitInfo.java create mode 100644 models/src/main/java/info/frostfs/sdk/mappers/object/SplitInfoMapper.java create mode 100644 models/src/test/java/info/frostfs/sdk/mappers/object/SplitInfoMapperTest.java diff --git a/README.md b/README.md index 737c204..03f013b 100644 --- a/README.md +++ b/README.md @@ -21,31 +21,40 @@ neo-go wallet export -w -d ### Container operations ```java +import info.frostfs.sdk.FrostFSClient; import info.frostfs.sdk.dto.container.Container; import info.frostfs.sdk.dto.netmap.PlacementPolicy; import info.frostfs.sdk.dto.netmap.Replica; import info.frostfs.sdk.enums.BasicAcl; import info.frostfs.sdk.jdo.ClientSettings; -import info.frostfs.sdk.FrostFSClient; +import info.frostfs.sdk.jdo.parameters.CallContext; +import info.frostfs.sdk.jdo.parameters.container.PrmContainerCreate; +import info.frostfs.sdk.jdo.parameters.container.PrmContainerDelete; +import info.frostfs.sdk.jdo.parameters.container.PrmContainerGet; +import info.frostfs.sdk.jdo.parameters.container.PrmContainerGetAll; public class ContainerExample { public void example() { + var callContext = new CallContext(); ClientSettings clientSettings = new ClientSettings(, ); FrostFSClient frostFSClient = new FrostFSClient(clientSettings); // Create container - var placementPolicy = new PlacementPolicy(new Replica[]{new Replica(1)}, Boolean.TRUE); - var containerId = frostFSClient.createContainer(new Container(BasicAcl.PUBLIC_RW, placementPolicy)); + var placementPolicy = new PlacementPolicy(new Replica[]{new Replica(1)}, true); + var prmContainerCreate = new PrmContainerCreate(new Container(BasicAcl.PUBLIC_RW, placementPolicy)); + var containerId = frostFSClient.createContainer(prmContainerCreate, callContext); // Get container - var container = frostFSClient.getContainer(containerId); + var prmContainerGet = new PrmContainerGet(containerId); + var container = frostFSClient.getContainer(prmContainerGet, callContext); // List containers - var containerIds = frostFSClient.listContainers(); + var containerIds = frostFSClient.listContainers(new PrmContainerGetAll(), callContext); // Delete container - frostFSClient.deleteContainer(containerId); + var prmContainerDelete = new PrmContainerDelete(containerId); + frostFSClient.deleteContainer(prmContainerDelete, callContext); } } ``` @@ -53,45 +62,104 @@ public class ContainerExample { ### Object operations ```java +import info.frostfs.sdk.dto.object.*; import info.frostfs.sdk.enums.ObjectType; -import info.frostfs.sdk.dto.container.ContainerId; -import info.frostfs.sdk.dto.object.ObjectAttribute; -import info.frostfs.sdk.dto.object.ObjectFilter; -import info.frostfs.sdk.dto.object.ObjectHeader; -import info.frostfs.sdk.dto.object.ObjectId; -import info.frostfs.sdk.jdo.PutObjectParameters; -import info.frostfs.sdk.FrostFSClient; +import info.frostfs.sdk.jdo.ClientSettings; +import info.frostfs.sdk.jdo.parameters.CallContext; +import info.frostfs.sdk.jdo.parameters.object.*; +import org.apache.commons.lang3.ArrayUtils; import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; +import static java.util.Objects.isNull; + public class ObjectExample { public void example() { + CallContext callContext = new CallContext(); ClientSettings clientSettings = new ClientSettings(, ); FrostFSClient frostFSClient = new FrostFSClient(clientSettings); // Put object ObjectId objectId; - try (FileInputStream fis = new FileInputStream("cat.jpg")) { - var cat = new ObjectHeader( - containerId, ObjectType.REGULAR, new ObjectAttribute[]{new ObjectAttribute("Filename", "cat.jpg")} - ); + try (FileInputStream file = new FileInputStream("/path/to/file/cat.jpg")) { + var attribute = new ObjectAttribute("Filename", "cat.jpg"); + var cat = new ObjectHeader(containerId, ObjectType.REGULAR, attribute); + var prmObjectPut = PrmObjectPut.builder().objectHeader(cat).build(); + var writer = frostFSClient.putObject(prmObjectPut, callContext); - var params = new PutObjectParameters(cat, fis); - objectId = frostFSClient.putObject(params); + writer.write(file.readAllBytes()); + objectId = writer.complete(); } catch (IOException e) { throw new RuntimeException(e); } // Get object - var obj = frostFSClient.getObject(containerId, objectId); + var prmObjectGet = new PrmObjectGet(containerId, oid); + ObjectFrostFS object = frostFSClient.getObject(prmObjectGet, callContext); + + var reader = object.getObjectReader(); + var chunk = reader.readChunk(); + var length = chunk.length; + byte[] buffer = null; + while (length > 0) { + buffer = isNull(buffer) ? chunk : ArrayHelper.concat(buffer, chunk); + + chunk = object.getObjectReader().readChunk(); + length = ArrayUtils.isEmpty(chunk) ? 0 : chunk.length; + } + + try (FileOutputStream fos = new FileOutputStream("/path/to/file/newCat.jpg")) { + fos.write(buffer); + } catch (Exception ignored) { + } // Get object header - var objectHeader = frostFSClient.getObjectHead(containerId, objectId); + var prmObjectHeadGet = new PrmObjectHeadGet(containerId, objectId); + var objectHeader = frostFSClient.getObjectHead(prmObjectHeadGet, callContext); // Search regular objects - var objectIds = frostFSClient.searchObjects(containerId, new ObjectFilter.FilterByRootObject()); + var prmObjectSearch = new PrmObjectSearch(containerId, new ObjectFilter.FilterByRootObject()); + var objectIds = frostFSClient.searchObjects(prmObjectSearch, callContext); + + // Delete object + var prmObjectDelete = new PrmObjectDelete(containerId, objectId); + frostFSClient.deleteObject(prmObjectDelete, callContext); + } +} +``` + +### Pool init + +```java +import info.frostfs.sdk.jdo.ECDsa; +import info.frostfs.sdk.jdo.parameters.CallContext; +import info.frostfs.sdk.jdo.pool.NodeParameters; +import info.frostfs.sdk.jdo.pool.PoolInitParameters; +import info.frostfs.sdk.pool.Pool; + +public class PoolExample { + + public static void example() { + CallContext callContext = new CallContext(); + + //Init + var nodeParam1 = new NodeParameters(1, , 1); + var nodeParam2 = new NodeParameters(1, , 1); + var nodeParam3 = new NodeParameters(1, , 1); + var nodeParam4 = new NodeParameters(1, , 1); + + PoolInitParameters initParameters = new PoolInitParameters(); + initParameters.setKey(new ECDsa()); + initParameters.setNodeParams(new NodeParameters[]{nodeParam1, nodeParam2, nodeParam3, nodeParam4}); + + + Pool pool = new Pool(initParameters); + + //Dial (Required!) + pool.dial(callContext); } } ``` \ No newline at end of file diff --git a/client/pom.xml b/client/pom.xml index e73703d..f907964 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -6,7 +6,7 @@ info.frostfs.sdk frostfs-sdk-java - 0.2.0 + 0.3.0 client @@ -21,17 +21,17 @@ info.frostfs.sdk cryptography - 0.2.0 + 0.3.0 info.frostfs.sdk models - 0.2.0 + 0.3.0 info.frostfs.sdk exceptions - 0.2.0 + 0.3.0 commons-codec diff --git a/client/src/main/java/info/frostfs/sdk/FrostFSClient.java b/client/src/main/java/info/frostfs/sdk/FrostFSClient.java index cb93dbb..31a76b7 100644 --- a/client/src/main/java/info/frostfs/sdk/FrostFSClient.java +++ b/client/src/main/java/info/frostfs/sdk/FrostFSClient.java @@ -2,13 +2,11 @@ package info.frostfs.sdk; import frostfs.accounting.Types; import info.frostfs.sdk.dto.chain.Chain; -import info.frostfs.sdk.dto.chain.ChainTarget; import info.frostfs.sdk.dto.container.Container; import info.frostfs.sdk.dto.container.ContainerId; import info.frostfs.sdk.dto.netmap.NetmapSnapshot; import info.frostfs.sdk.dto.netmap.NodeInfo; import info.frostfs.sdk.dto.netmap.Version; -import info.frostfs.sdk.dto.object.ObjectFilter; import info.frostfs.sdk.dto.object.ObjectFrostFS; import info.frostfs.sdk.dto.object.ObjectHeader; import info.frostfs.sdk.dto.object.ObjectId; @@ -17,13 +15,24 @@ import info.frostfs.sdk.exceptions.ProcessFrostFSException; import info.frostfs.sdk.jdo.ClientEnvironment; import info.frostfs.sdk.jdo.ClientSettings; import info.frostfs.sdk.jdo.NetworkSettings; -import info.frostfs.sdk.jdo.PutObjectParameters; +import info.frostfs.sdk.jdo.parameters.CallContext; +import info.frostfs.sdk.jdo.parameters.ape.PrmApeChainAdd; +import info.frostfs.sdk.jdo.parameters.ape.PrmApeChainList; +import info.frostfs.sdk.jdo.parameters.ape.PrmApeChainRemove; +import info.frostfs.sdk.jdo.parameters.container.PrmContainerCreate; +import info.frostfs.sdk.jdo.parameters.container.PrmContainerDelete; +import info.frostfs.sdk.jdo.parameters.container.PrmContainerGet; +import info.frostfs.sdk.jdo.parameters.container.PrmContainerGetAll; +import info.frostfs.sdk.jdo.parameters.object.*; +import info.frostfs.sdk.jdo.parameters.session.PrmSessionCreate; +import info.frostfs.sdk.jdo.result.ObjectHeaderResult; import info.frostfs.sdk.pool.SessionCache; import info.frostfs.sdk.pool.WrapperPrm; import info.frostfs.sdk.services.CommonClient; import info.frostfs.sdk.services.impl.*; import info.frostfs.sdk.services.impl.interceptor.Configuration; import info.frostfs.sdk.services.impl.interceptor.MonitoringClientInterceptor; +import info.frostfs.sdk.services.impl.rwhelper.ObjectWriter; import info.frostfs.sdk.utils.Validator; import io.grpc.Channel; import io.grpc.ClientInterceptors; @@ -55,8 +64,10 @@ public class FrostFSClient implements CommonClient { : initGrpcChannel(clientSettings); Channel interceptChannel = ClientInterceptors.intercept(channel, MONITORING_CLIENT_INTERCEPTOR); - ClientEnvironment clientEnvironment = - new ClientEnvironment(clientSettings.getKey(), interceptChannel, new Version(), this); + ClientEnvironment clientEnvironment = new ClientEnvironment( + clientSettings.getKey(), interceptChannel, new Version(), this, + new SessionCache(0) + ); Validator.validate(clientEnvironment); @@ -75,7 +86,7 @@ public class FrostFSClient implements CommonClient { Channel interceptChannel = ClientInterceptors.intercept(channel, MONITORING_CLIENT_INTERCEPTOR); ClientEnvironment clientEnvironment = - new ClientEnvironment(prm.getKey(), interceptChannel, new Version(), this); + new ClientEnvironment(prm.getKey(), interceptChannel, new Version(), this, cache); Validator.validate(clientEnvironment); @@ -90,7 +101,7 @@ public class FrostFSClient implements CommonClient { } private void checkFrostFSVersionSupport(Version version) { - var localNodeInfo = netmapClientImpl.getLocalNodeInfo(); + var localNodeInfo = netmapClientImpl.getLocalNodeInfo(new CallContext()); if (!localNodeInfo.getVersion().isSupported(version)) { throw new ProcessFrostFSException( String.format(VERSION_UNSUPPORTED_TEMPLATE, localNodeInfo.getVersion()) @@ -99,92 +110,97 @@ public class FrostFSClient implements CommonClient { } @Override - public Container getContainer(ContainerId cid) { - return containerClientImpl.getContainer(cid); + public Container getContainer(PrmContainerGet args, CallContext ctx) { + return containerClientImpl.getContainer(args, ctx); } @Override - public List listContainers() { - return containerClientImpl.listContainers(); + public List listContainers(PrmContainerGetAll args, CallContext ctx) { + return containerClientImpl.listContainers(args, ctx); } @Override - public ContainerId createContainer(Container container) { - return containerClientImpl.createContainer(container); + public ContainerId createContainer(PrmContainerCreate args, CallContext ctx) { + return containerClientImpl.createContainer(args, ctx); } @Override - public void deleteContainer(ContainerId cid) { - containerClientImpl.deleteContainer(cid); + public void deleteContainer(PrmContainerDelete args, CallContext ctx) { + containerClientImpl.deleteContainer(args, ctx); } @Override - public ObjectHeader getObjectHead(ContainerId containerId, ObjectId objectId) { - return objectClientImpl.getObjectHead(containerId, objectId); + public ObjectHeaderResult getObjectHead(PrmObjectHeadGet args, CallContext ctx) { + return objectClientImpl.getObjectHead(args, ctx); } @Override - public ObjectFrostFS getObject(ContainerId containerId, ObjectId objectId) { - return objectClientImpl.getObject(containerId, objectId); + public ObjectFrostFS getObject(PrmObjectGet args, CallContext ctx) { + return objectClientImpl.getObject(args, ctx); } @Override - public ObjectId putObject(PutObjectParameters parameters) { - return objectClientImpl.putObject(parameters); + public ObjectWriter putObject(PrmObjectPut args, CallContext ctx) { + return objectClientImpl.putObject(args, ctx); } @Override - public ObjectId putSingleObject(ObjectFrostFS objectFrostFS) { - return objectClientImpl.putSingleObject(objectFrostFS); + public ObjectId putClientCutObject(PrmObjectClientCutPut args, CallContext ctx) { + return objectClientImpl.putClientCutObject(args, ctx); } @Override - public void deleteObject(ContainerId containerId, ObjectId objectId) { - objectClientImpl.deleteObject(containerId, objectId); + public ObjectId putSingleObject(PrmObjectSinglePut args, CallContext ctx) { + return objectClientImpl.putSingleObject(args, ctx); } @Override - public Iterable searchObjects(ContainerId cid, ObjectFilter... filters) { - return objectClientImpl.searchObjects(cid, filters); + public void deleteObject(PrmObjectDelete args, CallContext ctx) { + objectClientImpl.deleteObject(args, ctx); } @Override - public byte[] addChain(Chain chain, ChainTarget chainTarget) { - return apeManagerClient.addChain(chain, chainTarget); + public Iterable searchObjects(PrmObjectSearch args, CallContext ctx) { + return objectClientImpl.searchObjects(args, ctx); } @Override - public void removeChain(Chain chain, ChainTarget chainTarget) { - apeManagerClient.removeChain(chain, chainTarget); + public byte[] addChain(PrmApeChainAdd args, CallContext ctx) { + return apeManagerClient.addChain(args, ctx); } @Override - public List listChains(ChainTarget chainTarget) { - return apeManagerClient.listChains(chainTarget); + public void removeChain(PrmApeChainRemove args, CallContext ctx) { + apeManagerClient.removeChain(args, ctx); } @Override - public NetmapSnapshot getNetmapSnapshot() { - return netmapClientImpl.getNetmapSnapshot(); + public List listChains(PrmApeChainList args, CallContext ctx) { + return apeManagerClient.listChains(args, ctx); } @Override - public NodeInfo getLocalNodeInfo() { - return netmapClientImpl.getLocalNodeInfo(); + public NetmapSnapshot getNetmapSnapshot(CallContext ctx) { + return netmapClientImpl.getNetmapSnapshot(ctx); } @Override - public NetworkSettings getNetworkSettings() { - return netmapClientImpl.getNetworkSettings(); + public NodeInfo getLocalNodeInfo(CallContext ctx) { + return netmapClientImpl.getLocalNodeInfo(ctx); } @Override - public SessionToken createSession(long expiration) { - return sessionClientImpl.createSession(expiration); + public NetworkSettings getNetworkSettings(CallContext ctx) { + return netmapClientImpl.getNetworkSettings(ctx); } - public frostfs.session.Types.SessionToken createSessionInternal(long expiration) { - return sessionClientImpl.createSessionInternal(expiration); + @Override + public SessionToken createSession(PrmSessionCreate args, CallContext ctx) { + return sessionClientImpl.createSession(args, ctx); + } + + public frostfs.session.Types.SessionToken createSessionInternal(PrmSessionCreate args, CallContext ctx) { + return sessionClientImpl.createSessionInternal(args, ctx); } @Override @@ -193,12 +209,12 @@ public class FrostFSClient implements CommonClient { } @Override - public Types.Decimal getBalance() { - return accountingClient.getBalance(); + public Types.Decimal getBalance(CallContext ctx) { + return accountingClient.getBalance(ctx); } - public String dial() { - accountingClient.getBalance(); + public String dial(CallContext ctx) { + accountingClient.getBalance(ctx); return null; } diff --git a/client/src/main/java/info/frostfs/sdk/jdo/ClientEnvironment.java b/client/src/main/java/info/frostfs/sdk/jdo/ClientEnvironment.java index 015ba6d..62e0967 100644 --- a/client/src/main/java/info/frostfs/sdk/jdo/ClientEnvironment.java +++ b/client/src/main/java/info/frostfs/sdk/jdo/ClientEnvironment.java @@ -5,9 +5,14 @@ import info.frostfs.sdk.annotations.NotNull; import info.frostfs.sdk.annotations.Validate; import info.frostfs.sdk.dto.netmap.Version; import info.frostfs.sdk.dto.object.OwnerId; +import info.frostfs.sdk.pool.SessionCache; import io.grpc.Channel; import lombok.Getter; import lombok.Setter; +import org.apache.commons.lang3.StringUtils; + +import static info.frostfs.sdk.Helper.getHexString; +import static info.frostfs.sdk.pool.Pool.formCacheKey; @Getter @Setter @@ -15,35 +20,48 @@ public class ClientEnvironment { @NotNull private final OwnerId ownerId; - @NotNull private final Version version; - @NotNull @Validate private final ECDsa key; - @NotNull private final Channel channel; - @NotNull private final FrostFSClient frostFSClient; + private String sessionKey; + private String address; private NetworkSettings networkSettings; - public ClientEnvironment(String wif, Channel channel, Version version, FrostFSClient frostFSClient) { + private SessionCache sessionCache; + + public ClientEnvironment(String wif, Channel channel, Version version, FrostFSClient frostFSClient, + SessionCache sessionCache) { this.key = new ECDsa(wif); this.ownerId = new OwnerId(key.getPublicKeyByte()); this.version = version; this.channel = channel; this.frostFSClient = frostFSClient; + this.sessionCache = sessionCache; + this.address = channel.authority(); } - public ClientEnvironment(ECDsa key, Channel channel, Version version, FrostFSClient frostFSClient) { + public ClientEnvironment(ECDsa key, Channel channel, Version version, FrostFSClient frostFSClient, + SessionCache sessionCache) { this.key = key; this.ownerId = new OwnerId(key.getPublicKeyByte()); this.version = version; this.channel = channel; this.frostFSClient = frostFSClient; + this.sessionCache = sessionCache; + } + + public String getSessionKey() { + if (StringUtils.isBlank(sessionKey)) { + this.sessionKey = formCacheKey(address, getHexString(key.getPublicKeyByte())); + } + + return sessionKey; } } diff --git a/client/src/main/java/info/frostfs/sdk/jdo/PutObjectParameters.java b/client/src/main/java/info/frostfs/sdk/jdo/PutObjectParameters.java deleted file mode 100644 index b995eac..0000000 --- a/client/src/main/java/info/frostfs/sdk/jdo/PutObjectParameters.java +++ /dev/null @@ -1,40 +0,0 @@ -package info.frostfs.sdk.jdo; - -import info.frostfs.sdk.annotations.NotNull; -import info.frostfs.sdk.dto.object.ObjectHeader; -import info.frostfs.sdk.dto.session.SessionToken; -import lombok.Getter; -import lombok.Setter; - -import java.io.InputStream; - -@Getter -@Setter -public class PutObjectParameters { - - @NotNull - private ObjectHeader header; - - @NotNull - private InputStream payload; - - private boolean clientCut; - private int bufferMaxSize; - private byte[] customerBuffer; - private SessionToken sessionToken; - private int maxObjectSizeCache; - private long currentStreamPosition; - private long fullLength; - - public PutObjectParameters(ObjectHeader header, InputStream payload, boolean clientCut, int bufferMaxSize) { - this.header = header; - this.payload = payload; - this.clientCut = clientCut; - this.bufferMaxSize = bufferMaxSize; - } - - public PutObjectParameters(ObjectHeader header, InputStream payload) { - this.header = header; - this.payload = payload; - } -} diff --git a/client/src/main/java/info/frostfs/sdk/jdo/parameters/CallContext.java b/client/src/main/java/info/frostfs/sdk/jdo/parameters/CallContext.java new file mode 100644 index 0000000..4831e99 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/jdo/parameters/CallContext.java @@ -0,0 +1,23 @@ +package info.frostfs.sdk.jdo.parameters; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +import java.util.concurrent.TimeUnit; + +import static info.frostfs.sdk.constants.AppConst.DEFAULT_GRPC_TIMEOUT; + +@Getter +@Builder +@AllArgsConstructor +public class CallContext { + private final long timeout; + private final TimeUnit timeUnit; + + public CallContext() { + this.timeout = DEFAULT_GRPC_TIMEOUT; + this.timeUnit = TimeUnit.SECONDS; + } +} + diff --git a/client/src/main/java/info/frostfs/sdk/jdo/WaitParameters.java b/client/src/main/java/info/frostfs/sdk/jdo/parameters/PrmWait.java similarity index 86% rename from client/src/main/java/info/frostfs/sdk/jdo/WaitParameters.java rename to client/src/main/java/info/frostfs/sdk/jdo/parameters/PrmWait.java index 7fdd965..c109bd4 100644 --- a/client/src/main/java/info/frostfs/sdk/jdo/WaitParameters.java +++ b/client/src/main/java/info/frostfs/sdk/jdo/parameters/PrmWait.java @@ -1,4 +1,4 @@ -package info.frostfs.sdk.jdo; +package info.frostfs.sdk.jdo.parameters; import lombok.AllArgsConstructor; import lombok.Getter; @@ -8,14 +8,14 @@ import java.time.LocalDateTime; @Getter @AllArgsConstructor -public class WaitParameters { +public class PrmWait { private static final Duration DEFAULT_TIMEOUT = Duration.ofSeconds(120); private static final Duration DEFAULT_POLL_INTERVAL = Duration.ofSeconds(5); private final Duration timeout; private final Duration pollInterval; - public WaitParameters() { + public PrmWait() { this.timeout = DEFAULT_TIMEOUT; this.pollInterval = DEFAULT_POLL_INTERVAL; } diff --git a/client/src/main/java/info/frostfs/sdk/jdo/parameters/ape/PrmApeChainAdd.java b/client/src/main/java/info/frostfs/sdk/jdo/parameters/ape/PrmApeChainAdd.java new file mode 100644 index 0000000..4692ac2 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/jdo/parameters/ape/PrmApeChainAdd.java @@ -0,0 +1,27 @@ +package info.frostfs.sdk.jdo.parameters.ape; + +import info.frostfs.sdk.annotations.NotNull; +import info.frostfs.sdk.dto.chain.Chain; +import info.frostfs.sdk.dto.chain.ChainTarget; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +import java.util.Map; + +@Getter +@Builder +@AllArgsConstructor +public class PrmApeChainAdd { + @NotNull + private Chain chain; + @NotNull + private ChainTarget chainTarget; + + private Map xHeaders; + + public PrmApeChainAdd(Chain chain, ChainTarget chainTarget) { + this.chain = chain; + this.chainTarget = chainTarget; + } +} diff --git a/client/src/main/java/info/frostfs/sdk/jdo/parameters/ape/PrmApeChainList.java b/client/src/main/java/info/frostfs/sdk/jdo/parameters/ape/PrmApeChainList.java new file mode 100644 index 0000000..70dc193 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/jdo/parameters/ape/PrmApeChainList.java @@ -0,0 +1,23 @@ +package info.frostfs.sdk.jdo.parameters.ape; + +import info.frostfs.sdk.annotations.NotNull; +import info.frostfs.sdk.dto.chain.ChainTarget; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +import java.util.Map; + +@Getter +@Builder +@AllArgsConstructor +public class PrmApeChainList { + @NotNull + private ChainTarget chainTarget; + + private Map xHeaders; + + public PrmApeChainList(ChainTarget chainTarget) { + this.chainTarget = chainTarget; + } +} diff --git a/client/src/main/java/info/frostfs/sdk/jdo/parameters/ape/PrmApeChainRemove.java b/client/src/main/java/info/frostfs/sdk/jdo/parameters/ape/PrmApeChainRemove.java new file mode 100644 index 0000000..93f53d3 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/jdo/parameters/ape/PrmApeChainRemove.java @@ -0,0 +1,27 @@ +package info.frostfs.sdk.jdo.parameters.ape; + +import info.frostfs.sdk.annotations.NotNull; +import info.frostfs.sdk.dto.chain.Chain; +import info.frostfs.sdk.dto.chain.ChainTarget; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +import java.util.Map; + +@Getter +@Builder +@AllArgsConstructor +public class PrmApeChainRemove { + @NotNull + private Chain chain; + @NotNull + private ChainTarget chainTarget; + + private Map xHeaders; + + public PrmApeChainRemove(Chain chain, ChainTarget chainTarget) { + this.chain = chain; + this.chainTarget = chainTarget; + } +} diff --git a/client/src/main/java/info/frostfs/sdk/jdo/parameters/container/PrmContainerCreate.java b/client/src/main/java/info/frostfs/sdk/jdo/parameters/container/PrmContainerCreate.java new file mode 100644 index 0000000..758bfd8 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/jdo/parameters/container/PrmContainerCreate.java @@ -0,0 +1,33 @@ +package info.frostfs.sdk.jdo.parameters.container; + +import info.frostfs.sdk.annotations.NotNull; +import info.frostfs.sdk.dto.container.Container; +import info.frostfs.sdk.dto.session.SessionToken; +import info.frostfs.sdk.jdo.parameters.PrmWait; +import info.frostfs.sdk.jdo.parameters.session.SessionContext; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +import java.util.Map; + +@Getter +@Builder +@AllArgsConstructor +public class PrmContainerCreate implements SessionContext { + @NotNull + private Container container; + + private PrmWait waitParams; + private SessionToken sessionToken; + private Map xHeaders; + + public PrmContainerCreate(Container container, PrmWait waitParams) { + this.container = container; + this.waitParams = waitParams; + } + + public PrmContainerCreate(Container container) { + this.container = container; + } +} diff --git a/client/src/main/java/info/frostfs/sdk/jdo/parameters/container/PrmContainerDelete.java b/client/src/main/java/info/frostfs/sdk/jdo/parameters/container/PrmContainerDelete.java new file mode 100644 index 0000000..c9baeaf --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/jdo/parameters/container/PrmContainerDelete.java @@ -0,0 +1,33 @@ +package info.frostfs.sdk.jdo.parameters.container; + +import info.frostfs.sdk.annotations.NotNull; +import info.frostfs.sdk.dto.container.ContainerId; +import info.frostfs.sdk.dto.session.SessionToken; +import info.frostfs.sdk.jdo.parameters.PrmWait; +import info.frostfs.sdk.jdo.parameters.session.SessionContext; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +import java.util.Map; + +@Getter +@Builder +@AllArgsConstructor +public class PrmContainerDelete implements SessionContext { + @NotNull + private ContainerId containerId; + + private PrmWait waitParams; + private SessionToken sessionToken; + private Map xHeaders; + + public PrmContainerDelete(ContainerId containerId, PrmWait waitParams) { + this.containerId = containerId; + this.waitParams = waitParams; + } + + public PrmContainerDelete(ContainerId containerId) { + this.containerId = containerId; + } +} diff --git a/client/src/main/java/info/frostfs/sdk/jdo/parameters/container/PrmContainerGet.java b/client/src/main/java/info/frostfs/sdk/jdo/parameters/container/PrmContainerGet.java new file mode 100644 index 0000000..aec9924 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/jdo/parameters/container/PrmContainerGet.java @@ -0,0 +1,23 @@ +package info.frostfs.sdk.jdo.parameters.container; + +import info.frostfs.sdk.annotations.NotNull; +import info.frostfs.sdk.dto.container.ContainerId; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +import java.util.Map; + +@Getter +@Builder +@AllArgsConstructor +public class PrmContainerGet { + @NotNull + private ContainerId containerId; + + private Map xHeaders; + + public PrmContainerGet(ContainerId containerId) { + this.containerId = containerId; + } +} diff --git a/client/src/main/java/info/frostfs/sdk/jdo/parameters/container/PrmContainerGetAll.java b/client/src/main/java/info/frostfs/sdk/jdo/parameters/container/PrmContainerGetAll.java new file mode 100644 index 0000000..56b8d7b --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/jdo/parameters/container/PrmContainerGetAll.java @@ -0,0 +1,16 @@ +package info.frostfs.sdk.jdo.parameters.container; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.Map; + +@Getter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class PrmContainerGetAll { + private Map xHeaders; +} diff --git a/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/PrmObjectClientCutPut.java b/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/PrmObjectClientCutPut.java new file mode 100644 index 0000000..9703a4c --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/PrmObjectClientCutPut.java @@ -0,0 +1,28 @@ +package info.frostfs.sdk.jdo.parameters.object; + +import info.frostfs.sdk.annotations.NotNull; +import info.frostfs.sdk.dto.object.ObjectHeader; +import info.frostfs.sdk.dto.session.SessionToken; +import info.frostfs.sdk.jdo.parameters.session.SessionContext; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +import java.io.InputStream; +import java.util.Map; + +@Getter +@Builder +@AllArgsConstructor +public class PrmObjectClientCutPut implements PrmObjectPutBase, SessionContext { + @NotNull + private final PutObjectContext putObjectContext = new PutObjectContext(); + @NotNull + private ObjectHeader objectHeader; + @NotNull + private InputStream payload; + private int bufferMaxSize; + private byte[] customerBuffer; + private SessionToken sessionToken; + private Map xHeaders; +} diff --git a/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/PrmObjectDelete.java b/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/PrmObjectDelete.java new file mode 100644 index 0000000..326834f --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/PrmObjectDelete.java @@ -0,0 +1,30 @@ +package info.frostfs.sdk.jdo.parameters.object; + +import info.frostfs.sdk.annotations.NotNull; +import info.frostfs.sdk.dto.container.ContainerId; +import info.frostfs.sdk.dto.object.ObjectId; +import info.frostfs.sdk.dto.session.SessionToken; +import info.frostfs.sdk.jdo.parameters.session.SessionContext; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +import java.util.Map; + +@Getter +@Builder +@AllArgsConstructor +public class PrmObjectDelete implements SessionContext { + @NotNull + private ContainerId containerId; + @NotNull + private ObjectId objectId; + + private SessionToken sessionToken; + private Map xHeaders; + + public PrmObjectDelete(ContainerId containerId, ObjectId objectId) { + this.containerId = containerId; + this.objectId = objectId; + } +} diff --git a/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/PrmObjectGet.java b/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/PrmObjectGet.java new file mode 100644 index 0000000..e8eb3d7 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/PrmObjectGet.java @@ -0,0 +1,30 @@ +package info.frostfs.sdk.jdo.parameters.object; + +import info.frostfs.sdk.annotations.NotNull; +import info.frostfs.sdk.dto.container.ContainerId; +import info.frostfs.sdk.dto.object.ObjectId; +import info.frostfs.sdk.dto.session.SessionToken; +import info.frostfs.sdk.jdo.parameters.session.SessionContext; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +import java.util.Map; + +@Getter +@Builder +@AllArgsConstructor +public class PrmObjectGet implements SessionContext { + @NotNull + private ContainerId containerId; + @NotNull + private ObjectId objectId; + + private SessionToken sessionToken; + private Map xHeaders; + + public PrmObjectGet(ContainerId containerId, ObjectId objectId) { + this.containerId = containerId; + this.objectId = objectId; + } +} diff --git a/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/PrmObjectHeadGet.java b/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/PrmObjectHeadGet.java new file mode 100644 index 0000000..8263db7 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/PrmObjectHeadGet.java @@ -0,0 +1,31 @@ +package info.frostfs.sdk.jdo.parameters.object; + +import info.frostfs.sdk.annotations.NotNull; +import info.frostfs.sdk.dto.container.ContainerId; +import info.frostfs.sdk.dto.object.ObjectId; +import info.frostfs.sdk.dto.session.SessionToken; +import info.frostfs.sdk.jdo.parameters.session.SessionContext; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +import java.util.Map; + +@Getter +@Builder +@AllArgsConstructor +public class PrmObjectHeadGet implements SessionContext { + @NotNull + private ContainerId containerId; + @NotNull + private ObjectId objectId; + + private boolean raw; + private SessionToken sessionToken; + private Map xHeaders; + + public PrmObjectHeadGet(ContainerId containerId, ObjectId objectId) { + this.containerId = containerId; + this.objectId = objectId; + } +} diff --git a/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/PrmObjectPut.java b/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/PrmObjectPut.java new file mode 100644 index 0000000..ac99504 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/PrmObjectPut.java @@ -0,0 +1,28 @@ +package info.frostfs.sdk.jdo.parameters.object; + +import info.frostfs.sdk.annotations.NotNull; +import info.frostfs.sdk.dto.object.ObjectHeader; +import info.frostfs.sdk.dto.session.SessionToken; +import info.frostfs.sdk.jdo.parameters.session.SessionContext; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +import java.util.Map; + +@Getter +@Builder +@AllArgsConstructor +public class PrmObjectPut implements PrmObjectPutBase, SessionContext { + @NotNull + private final PutObjectContext putObjectContext = new PutObjectContext(); + @NotNull + private ObjectHeader objectHeader; + + private SessionToken sessionToken; + private Map xHeaders; + + public PrmObjectPut(ObjectHeader objectHeader) { + this.objectHeader = objectHeader; + } +} diff --git a/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/PrmObjectPutBase.java b/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/PrmObjectPutBase.java new file mode 100644 index 0000000..7ddfafb --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/PrmObjectPutBase.java @@ -0,0 +1,12 @@ +package info.frostfs.sdk.jdo.parameters.object; + +import info.frostfs.sdk.dto.object.ObjectHeader; +import info.frostfs.sdk.jdo.parameters.session.SessionContext; + +import java.util.Map; + +public interface PrmObjectPutBase extends SessionContext { + ObjectHeader getObjectHeader(); + + Map getXHeaders(); +} diff --git a/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/PrmObjectSearch.java b/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/PrmObjectSearch.java new file mode 100644 index 0000000..0bc414b --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/PrmObjectSearch.java @@ -0,0 +1,30 @@ +package info.frostfs.sdk.jdo.parameters.object; + +import info.frostfs.sdk.annotations.NotNull; +import info.frostfs.sdk.dto.container.ContainerId; +import info.frostfs.sdk.dto.object.ObjectFilter; +import info.frostfs.sdk.dto.session.SessionToken; +import info.frostfs.sdk.jdo.parameters.session.SessionContext; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +import java.util.Map; + +@Getter +@Builder +@AllArgsConstructor +public class PrmObjectSearch implements SessionContext { + @NotNull + private ContainerId containerId; + @NotNull + private ObjectFilter[] filters; + + private SessionToken sessionToken; + private Map xHeaders; + + public PrmObjectSearch(ContainerId containerId, ObjectFilter... filters) { + this.containerId = containerId; + this.filters = filters; + } +} diff --git a/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/PrmObjectSinglePut.java b/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/PrmObjectSinglePut.java new file mode 100644 index 0000000..2965d0c --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/PrmObjectSinglePut.java @@ -0,0 +1,26 @@ +package info.frostfs.sdk.jdo.parameters.object; + +import info.frostfs.sdk.annotations.NotNull; +import info.frostfs.sdk.dto.object.ObjectFrostFS; +import info.frostfs.sdk.dto.session.SessionToken; +import info.frostfs.sdk.jdo.parameters.session.SessionContext; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +import java.util.Map; + +@Getter +@Builder +@AllArgsConstructor +public class PrmObjectSinglePut implements SessionContext { + @NotNull + private ObjectFrostFS objectFrostFS; + + private SessionToken sessionToken; + private Map xHeaders; + + public PrmObjectSinglePut(ObjectFrostFS objectFrostFS) { + this.objectFrostFS = objectFrostFS; + } +} diff --git a/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/PutObjectContext.java b/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/PutObjectContext.java new file mode 100644 index 0000000..fc97dd0 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/PutObjectContext.java @@ -0,0 +1,14 @@ +package info.frostfs.sdk.jdo.parameters.object; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +public class PutObjectContext { + private int maxObjectSizeCache; + private long currentStreamPosition; + private long fullLength; +} diff --git a/client/src/main/java/info/frostfs/sdk/jdo/parameters/session/PrmSessionCreate.java b/client/src/main/java/info/frostfs/sdk/jdo/parameters/session/PrmSessionCreate.java new file mode 100644 index 0000000..c73dd2b --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/jdo/parameters/session/PrmSessionCreate.java @@ -0,0 +1,19 @@ +package info.frostfs.sdk.jdo.parameters.session; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +import java.util.Map; + +@Getter +@Builder +@AllArgsConstructor +public class PrmSessionCreate { + private long expiration; //-1 is max + private Map xHeaders; + + public PrmSessionCreate(long expiration) { + this.expiration = expiration; + } +} diff --git a/client/src/main/java/info/frostfs/sdk/jdo/parameters/session/SessionContext.java b/client/src/main/java/info/frostfs/sdk/jdo/parameters/session/SessionContext.java new file mode 100644 index 0000000..536017a --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/jdo/parameters/session/SessionContext.java @@ -0,0 +1,7 @@ +package info.frostfs.sdk.jdo.parameters.session; + +import info.frostfs.sdk.dto.session.SessionToken; + +public interface SessionContext { + SessionToken getSessionToken(); +} diff --git a/client/src/main/java/info/frostfs/sdk/jdo/result/ObjectHeaderResult.java b/client/src/main/java/info/frostfs/sdk/jdo/result/ObjectHeaderResult.java new file mode 100644 index 0000000..a3fbb1e --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/jdo/result/ObjectHeaderResult.java @@ -0,0 +1,15 @@ +package info.frostfs.sdk.jdo.result; + +import info.frostfs.sdk.dto.object.ObjectHeader; +import info.frostfs.sdk.dto.object.SplitInfo; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Builder +@Getter +@Setter +public class ObjectHeaderResult { + private ObjectHeader headerInfo; + private SplitInfo splitInfo; +} diff --git a/client/src/main/java/info/frostfs/sdk/pool/ClientWrapper.java b/client/src/main/java/info/frostfs/sdk/pool/ClientWrapper.java index 0189cc1..54163d1 100644 --- a/client/src/main/java/info/frostfs/sdk/pool/ClientWrapper.java +++ b/client/src/main/java/info/frostfs/sdk/pool/ClientWrapper.java @@ -4,6 +4,7 @@ import info.frostfs.sdk.FrostFSClient; import info.frostfs.sdk.enums.MethodIndex; import info.frostfs.sdk.exceptions.ResponseFrostFSException; import info.frostfs.sdk.exceptions.ValidationFrostFSException; +import info.frostfs.sdk.jdo.parameters.CallContext; import info.frostfs.sdk.utils.WaitUtil; import lombok.AccessLevel; import lombok.Getter; @@ -45,12 +46,12 @@ public class ClientWrapper extends ClientStatusMonitor { } } - public void dial() { + public void dial(CallContext ctx) { FrostFSClient client = getClient(); if (client == null) { throw new ValidationFrostFSException(POOL_CLIENT_UNHEALTHY); } - client.dial(); + client.dial(ctx); } public void handleError(Exception exp) { @@ -77,9 +78,9 @@ public class ClientWrapper extends ClientStatusMonitor { client.close(); } - public CompletableFuture restartIfUnhealthy() { + public CompletableFuture restartIfUnhealthy(CallContext ctx) { try { - client.getLocalNodeInfo(); + client.getLocalNodeInfo(ctx); return CompletableFuture.completedFuture(false); } catch (Exception ignored) { } @@ -88,15 +89,15 @@ public class ClientWrapper extends ClientStatusMonitor { scheduleGracefulClose(); } - return CompletableFuture.completedFuture(restartClient()); + return CompletableFuture.completedFuture(restartClient(ctx)); } - private boolean restartClient() { + private boolean restartClient(CallContext ctx) { FrostFSClient newClient = null; try { newClient = new FrostFSClient(wrapperPrm, sessionCache); - var error = newClient.dial(); + var error = newClient.dial(ctx); if (StringUtils.isNotBlank(error)) { setUnhealthyOnDial(); newClient.close(); @@ -113,7 +114,7 @@ public class ClientWrapper extends ClientStatusMonitor { } try { - client.getLocalNodeInfo(); + client.getLocalNodeInfo(ctx); } catch (Exception exp) { setUnhealthy(); return true; diff --git a/client/src/main/java/info/frostfs/sdk/pool/Pool.java b/client/src/main/java/info/frostfs/sdk/pool/Pool.java index 3306ea5..74143a2 100644 --- a/client/src/main/java/info/frostfs/sdk/pool/Pool.java +++ b/client/src/main/java/info/frostfs/sdk/pool/Pool.java @@ -2,12 +2,14 @@ package info.frostfs.sdk.pool; import frostfs.refs.Types; import info.frostfs.sdk.dto.chain.Chain; -import info.frostfs.sdk.dto.chain.ChainTarget; import info.frostfs.sdk.dto.container.Container; import info.frostfs.sdk.dto.container.ContainerId; import info.frostfs.sdk.dto.netmap.NetmapSnapshot; import info.frostfs.sdk.dto.netmap.NodeInfo; -import info.frostfs.sdk.dto.object.*; +import info.frostfs.sdk.dto.object.ObjectFrostFS; +import info.frostfs.sdk.dto.object.ObjectHeader; +import info.frostfs.sdk.dto.object.ObjectId; +import info.frostfs.sdk.dto.object.OwnerId; import info.frostfs.sdk.dto.session.SessionToken; import info.frostfs.sdk.exceptions.FrostFSException; import info.frostfs.sdk.exceptions.SessionExpiredFrostFSException; @@ -15,10 +17,21 @@ import info.frostfs.sdk.exceptions.SessionNotFoundFrostFSException; import info.frostfs.sdk.exceptions.ValidationFrostFSException; import info.frostfs.sdk.jdo.ECDsa; import info.frostfs.sdk.jdo.NetworkSettings; -import info.frostfs.sdk.jdo.PutObjectParameters; +import info.frostfs.sdk.jdo.parameters.CallContext; +import info.frostfs.sdk.jdo.parameters.ape.PrmApeChainAdd; +import info.frostfs.sdk.jdo.parameters.ape.PrmApeChainList; +import info.frostfs.sdk.jdo.parameters.ape.PrmApeChainRemove; +import info.frostfs.sdk.jdo.parameters.container.PrmContainerCreate; +import info.frostfs.sdk.jdo.parameters.container.PrmContainerDelete; +import info.frostfs.sdk.jdo.parameters.container.PrmContainerGet; +import info.frostfs.sdk.jdo.parameters.container.PrmContainerGetAll; +import info.frostfs.sdk.jdo.parameters.object.*; +import info.frostfs.sdk.jdo.parameters.session.PrmSessionCreate; import info.frostfs.sdk.jdo.pool.NodeParameters; import info.frostfs.sdk.jdo.pool.PoolInitParameters; +import info.frostfs.sdk.jdo.result.ObjectHeaderResult; import info.frostfs.sdk.services.CommonClient; +import info.frostfs.sdk.services.impl.rwhelper.ObjectWriter; import info.frostfs.sdk.utils.FrostFSMessages; import info.frostfs.sdk.utils.WaitUtil; import lombok.Getter; @@ -171,21 +184,21 @@ public class Pool implements CommonClient { } } - private static SessionToken initSessionForDuration(ClientWrapper cw, long duration) { + private static SessionToken initSessionForDuration(CallContext ctx, ClientWrapper cw, long duration) { var client = cw.getClient(); - NetworkSettings networkInfo = client.getNetworkSettings(); + NetworkSettings networkInfo = client.getNetworkSettings(ctx); long epoch = networkInfo.getEpochDuration(); long exp = (Long.MAX_VALUE - epoch < duration) ? Long.MAX_VALUE : (epoch + duration); - return client.createSession(exp); + return client.createSession(new PrmSessionCreate(exp), ctx); } - private static String formCacheKey(String address, String key) { + public static String formCacheKey(String address, String key) { return address + key; } - public String dial() { + public String dial(CallContext ctx) { InnerPool[] inner = new InnerPool[rebalanceParams.getNodesParams().length]; boolean atLeastOneHealthy = false; int i = 0; @@ -198,10 +211,12 @@ public class Pool implements CommonClient { boolean dialed = false; try { - client.dial(); + client.dial(ctx); dialed = true; - SessionToken token = initSessionForDuration(client, rebalanceParams.getSessionExpirationDuration()); + SessionToken token = initSessionForDuration( + ctx, client, rebalanceParams.getSessionExpirationDuration() + ); String cacheKey = formCacheKey( nodeParams.getAddress().get(j), getHexString(key.getPublicKeyByte()) @@ -236,10 +251,10 @@ public class Pool implements CommonClient { this.innerPools = inner; - NetworkSettings networkSettings = getNetworkSettings(); + NetworkSettings networkSettings = getNetworkSettings(ctx); this.maxObjectSize = networkSettings.getMaxObjectSize(); - startRebalance(); + startRebalance(ctx); return null; } @@ -267,7 +282,7 @@ public class Pool implements CommonClient { } } - public void startRebalance() { + public void startRebalance(CallContext ctx) { double[][] buffers = new double[rebalanceParams.getNodesParams().length][]; for (int i = 0; i < rebalanceParams.getNodesParams().length; i++) { @@ -276,24 +291,24 @@ public class Pool implements CommonClient { CompletableFuture.runAsync(() -> { WaitUtil.sleep(rebalanceParams.getClientRebalanceInterval()); - updateNodesHealth(buffers); + updateNodesHealth(ctx, buffers); }); } } - private void updateNodesHealth(double[][] buffers) { + private void updateNodesHealth(CallContext ctx, double[][] buffers) { CompletableFuture[] tasks = new CompletableFuture[innerPools.length]; for (int i = 0; i < innerPools.length; i++) { double[] bufferWeights = buffers[i]; int finalI = i; - tasks[i] = CompletableFuture.runAsync(() -> updateInnerNodesHealth(finalI, bufferWeights)); + tasks[i] = CompletableFuture.runAsync(() -> updateInnerNodesHealth(ctx, finalI, bufferWeights)); } CompletableFuture.allOf(tasks).join(); } - private void updateInnerNodesHealth(int poolIndex, double[] bufferWeights) { + private void updateInnerNodesHealth(CallContext ctx, int poolIndex, double[] bufferWeights) { if (poolIndex > innerPools.length - 1) { return; } @@ -311,7 +326,7 @@ public class Pool implements CommonClient { AtomicBoolean changed = new AtomicBoolean(false); int finalJ = j; - tasks[j] = client.restartIfUnhealthy().handle((unused, throwable) -> { + tasks[j] = client.restartIfUnhealthy(ctx).handle((unused, throwable) -> { if (throwable != null) { error.set(throwable.getMessage()); bufferWeights[finalJ] = 0; @@ -398,112 +413,112 @@ public class Pool implements CommonClient { return statistics; } - @Override - public Container getContainer(ContainerId cid) { + public Container getContainer(PrmContainerGet args, CallContext ctx) { ClientWrapper client = connection(); - return client.getClient().getContainer(cid); + return client.getClient().getContainer(args, ctx); } @Override - public List listContainers() { + public List listContainers(PrmContainerGetAll args, CallContext ctx) { ClientWrapper client = connection(); - return client.getClient().listContainers(); + return client.getClient().listContainers(args, ctx); } @Override - public ContainerId createContainer(Container container) { + public ContainerId createContainer(PrmContainerCreate args, CallContext ctx) { ClientWrapper client = connection(); - return client.getClient().createContainer(container); + return client.getClient().createContainer(args, ctx); } @Override - public void deleteContainer(ContainerId cid) { + public void deleteContainer(PrmContainerDelete args, CallContext ctx) { ClientWrapper client = connection(); - client.getClient().deleteContainer(cid); + client.getClient().deleteContainer(args, ctx); } @Override - public ObjectHeader getObjectHead(ContainerId containerId, ObjectId objectId) { + public ObjectHeaderResult getObjectHead(PrmObjectHeadGet args, CallContext ctx) { ClientWrapper client = connection(); - return client.getClient().getObjectHead(containerId, objectId); + return client.getClient().getObjectHead(args, ctx); } @Override - public ObjectFrostFS getObject(ContainerId containerId, ObjectId objectId) { + public ObjectFrostFS getObject(PrmObjectGet args, CallContext ctx) { ClientWrapper client = connection(); - return client.getClient().getObject(containerId, objectId); + return client.getClient().getObject(args, ctx); } @Override - public ObjectId putObject(PutObjectParameters parameters) { + public ObjectWriter putObject(PrmObjectPut args, CallContext ctx) { ClientWrapper client = connection(); - return client.getClient().putObject(parameters); + return client.getClient().putObject(args, ctx); } @Override - public ObjectId putSingleObject(ObjectFrostFS objectFrostFS) { + public ObjectId putClientCutObject(PrmObjectClientCutPut args, CallContext ctx) { ClientWrapper client = connection(); - return client.getClient().putSingleObject(objectFrostFS); + return client.getClient().putClientCutObject(args, ctx); } @Override - public void deleteObject(ContainerId containerId, ObjectId objectId) { + public ObjectId putSingleObject(PrmObjectSinglePut args, CallContext ctx) { ClientWrapper client = connection(); - client.getClient().deleteObject(containerId, objectId); + return client.getClient().putSingleObject(args, ctx); } @Override - public Iterable searchObjects(ContainerId cid, ObjectFilter... filters) { + public void deleteObject(PrmObjectDelete args, CallContext ctx) { ClientWrapper client = connection(); - return client.getClient().searchObjects(cid, filters); + client.getClient().deleteObject(args, ctx); } @Override - public byte[] addChain(Chain chain, ChainTarget chainTarget) { + public Iterable searchObjects(PrmObjectSearch args, CallContext ctx) { ClientWrapper client = connection(); - return client.getClient().addChain(chain, chainTarget); + return client.getClient().searchObjects(args, ctx); } @Override - public void removeChain(Chain chain, ChainTarget chainTarget) { + public byte[] addChain(PrmApeChainAdd args, CallContext ctx) { ClientWrapper client = connection(); - client.getClient().removeChain(chain, chainTarget); + return client.getClient().addChain(args, ctx); } @Override - public List listChains(ChainTarget chainTarget) { + public void removeChain(PrmApeChainRemove args, CallContext ctx) { ClientWrapper client = connection(); - return client.getClient().listChains(chainTarget); + client.getClient().removeChain(args, ctx); } @Override - public NetmapSnapshot getNetmapSnapshot() { + public List listChains(PrmApeChainList args, CallContext ctx) { ClientWrapper client = connection(); - return client.getClient().getNetmapSnapshot(); + return client.getClient().listChains(args, ctx); } @Override - public NodeInfo getLocalNodeInfo() { + public NetmapSnapshot getNetmapSnapshot(CallContext ctx) { ClientWrapper client = connection(); - return client.getClient().getLocalNodeInfo(); + return client.getClient().getNetmapSnapshot(ctx); } @Override - public NetworkSettings getNetworkSettings() { + public NodeInfo getLocalNodeInfo(CallContext ctx) { ClientWrapper client = connection(); - return client.getClient().getNetworkSettings(); + return client.getClient().getLocalNodeInfo(ctx); } @Override - public SessionToken createSession(long expiration) { + public NetworkSettings getNetworkSettings(CallContext ctx) { ClientWrapper client = connection(); - return client.getClient().createSession(expiration); + return client.getClient().getNetworkSettings(ctx); } - public frostfs.session.Types.SessionToken createSessionInternal(long expiration) { + @Override + public SessionToken createSession(PrmSessionCreate args, CallContext ctx) { ClientWrapper client = connection(); - return client.getClient().createSessionInternal(expiration); + return client.getClient().createSession(args, ctx); } @Override @@ -513,8 +528,8 @@ public class Pool implements CommonClient { } @Override - public frostfs.accounting.Types.Decimal getBalance() { + public frostfs.accounting.Types.Decimal getBalance(CallContext ctx) { ClientWrapper client = connection(); - return client.getClient().getBalance(); + return client.getClient().getBalance(ctx); } } diff --git a/client/src/main/java/info/frostfs/sdk/pool/SessionCache.java b/client/src/main/java/info/frostfs/sdk/pool/SessionCache.java index f64865b..998beb6 100644 --- a/client/src/main/java/info/frostfs/sdk/pool/SessionCache.java +++ b/client/src/main/java/info/frostfs/sdk/pool/SessionCache.java @@ -1,6 +1,7 @@ package info.frostfs.sdk.pool; import info.frostfs.sdk.dto.session.SessionToken; +import org.apache.commons.lang3.StringUtils; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -18,18 +19,8 @@ public class SessionCache { return cache.containsKey(key); } - public boolean tryGetValue(String key, SessionToken[] value) { - if (key == null) { - value[0] = null; - return false; - } - - SessionToken token = cache.get(key); - if (token != null) { - value[0] = token; - return true; - } - return false; + public SessionToken tryGetValue(String key) { + return StringUtils.isBlank(key) ? null : cache.get(key); } public void setValue(String key, SessionToken value) { diff --git a/client/src/main/java/info/frostfs/sdk/services/AccountingClient.java b/client/src/main/java/info/frostfs/sdk/services/AccountingClient.java index e29e153..28c2516 100644 --- a/client/src/main/java/info/frostfs/sdk/services/AccountingClient.java +++ b/client/src/main/java/info/frostfs/sdk/services/AccountingClient.java @@ -1,7 +1,8 @@ package info.frostfs.sdk.services; import frostfs.accounting.Types; +import info.frostfs.sdk.jdo.parameters.CallContext; public interface AccountingClient { - Types.Decimal getBalance(); + Types.Decimal getBalance(CallContext ctx); } diff --git a/client/src/main/java/info/frostfs/sdk/services/ApeManagerClient.java b/client/src/main/java/info/frostfs/sdk/services/ApeManagerClient.java index 5c5263c..521d8af 100644 --- a/client/src/main/java/info/frostfs/sdk/services/ApeManagerClient.java +++ b/client/src/main/java/info/frostfs/sdk/services/ApeManagerClient.java @@ -1,14 +1,17 @@ package info.frostfs.sdk.services; import info.frostfs.sdk.dto.chain.Chain; -import info.frostfs.sdk.dto.chain.ChainTarget; +import info.frostfs.sdk.jdo.parameters.CallContext; +import info.frostfs.sdk.jdo.parameters.ape.PrmApeChainAdd; +import info.frostfs.sdk.jdo.parameters.ape.PrmApeChainList; +import info.frostfs.sdk.jdo.parameters.ape.PrmApeChainRemove; import java.util.List; public interface ApeManagerClient { - byte[] addChain(Chain chain, ChainTarget chainTarget); + byte[] addChain(PrmApeChainAdd args, CallContext ctx); - void removeChain(Chain chain, ChainTarget chainTarget); + void removeChain(PrmApeChainRemove args, CallContext ctx); - List listChains(ChainTarget chainTarget); + List listChains(PrmApeChainList args, CallContext ctx); } diff --git a/client/src/main/java/info/frostfs/sdk/services/ContainerClient.java b/client/src/main/java/info/frostfs/sdk/services/ContainerClient.java index d3f7f9c..121bc80 100644 --- a/client/src/main/java/info/frostfs/sdk/services/ContainerClient.java +++ b/client/src/main/java/info/frostfs/sdk/services/ContainerClient.java @@ -2,15 +2,20 @@ package info.frostfs.sdk.services; import info.frostfs.sdk.dto.container.Container; import info.frostfs.sdk.dto.container.ContainerId; +import info.frostfs.sdk.jdo.parameters.CallContext; +import info.frostfs.sdk.jdo.parameters.container.PrmContainerCreate; +import info.frostfs.sdk.jdo.parameters.container.PrmContainerDelete; +import info.frostfs.sdk.jdo.parameters.container.PrmContainerGet; +import info.frostfs.sdk.jdo.parameters.container.PrmContainerGetAll; import java.util.List; public interface ContainerClient { - Container getContainer(ContainerId cid); + Container getContainer(PrmContainerGet args, CallContext ctx); - List listContainers(); + List listContainers(PrmContainerGetAll args, CallContext ctx); - ContainerId createContainer(Container container); + ContainerId createContainer(PrmContainerCreate args, CallContext ctx); - void deleteContainer(ContainerId cid); + void deleteContainer(PrmContainerDelete args, CallContext ctx); } diff --git a/client/src/main/java/info/frostfs/sdk/services/NetmapClient.java b/client/src/main/java/info/frostfs/sdk/services/NetmapClient.java index bc34978..067c836 100644 --- a/client/src/main/java/info/frostfs/sdk/services/NetmapClient.java +++ b/client/src/main/java/info/frostfs/sdk/services/NetmapClient.java @@ -3,11 +3,12 @@ package info.frostfs.sdk.services; import info.frostfs.sdk.dto.netmap.NetmapSnapshot; import info.frostfs.sdk.dto.netmap.NodeInfo; import info.frostfs.sdk.jdo.NetworkSettings; +import info.frostfs.sdk.jdo.parameters.CallContext; public interface NetmapClient { - NetmapSnapshot getNetmapSnapshot(); + NetmapSnapshot getNetmapSnapshot(CallContext ctx); - NodeInfo getLocalNodeInfo(); + NodeInfo getLocalNodeInfo(CallContext ctx); - NetworkSettings getNetworkSettings(); + NetworkSettings getNetworkSettings(CallContext ctx); } diff --git a/client/src/main/java/info/frostfs/sdk/services/ObjectClient.java b/client/src/main/java/info/frostfs/sdk/services/ObjectClient.java index 3a38295..86901b5 100644 --- a/client/src/main/java/info/frostfs/sdk/services/ObjectClient.java +++ b/client/src/main/java/info/frostfs/sdk/services/ObjectClient.java @@ -1,22 +1,24 @@ package info.frostfs.sdk.services; -import info.frostfs.sdk.dto.container.ContainerId; -import info.frostfs.sdk.dto.object.ObjectFilter; import info.frostfs.sdk.dto.object.ObjectFrostFS; -import info.frostfs.sdk.dto.object.ObjectHeader; import info.frostfs.sdk.dto.object.ObjectId; -import info.frostfs.sdk.jdo.PutObjectParameters; +import info.frostfs.sdk.jdo.parameters.CallContext; +import info.frostfs.sdk.jdo.parameters.object.*; +import info.frostfs.sdk.jdo.result.ObjectHeaderResult; +import info.frostfs.sdk.services.impl.rwhelper.ObjectWriter; public interface ObjectClient { - ObjectHeader getObjectHead(ContainerId containerId, ObjectId objectId); + ObjectHeaderResult getObjectHead(PrmObjectHeadGet args, CallContext ctx); - ObjectFrostFS getObject(ContainerId containerId, ObjectId objectId); + ObjectFrostFS getObject(PrmObjectGet args, CallContext ctx); - ObjectId putObject(PutObjectParameters parameters); + ObjectWriter putObject(PrmObjectPut args, CallContext ctx); - ObjectId putSingleObject(ObjectFrostFS objectFrostFS); + ObjectId putClientCutObject(PrmObjectClientCutPut args, CallContext ctx); - void deleteObject(ContainerId containerId, ObjectId objectId); + ObjectId putSingleObject(PrmObjectSinglePut args, CallContext ctx); - Iterable searchObjects(ContainerId cid, ObjectFilter... filters); + void deleteObject(PrmObjectDelete args, CallContext ctx); + + Iterable searchObjects(PrmObjectSearch args, CallContext ctx); } diff --git a/client/src/main/java/info/frostfs/sdk/services/SessionClient.java b/client/src/main/java/info/frostfs/sdk/services/SessionClient.java index e9b197c..4e5f0a9 100644 --- a/client/src/main/java/info/frostfs/sdk/services/SessionClient.java +++ b/client/src/main/java/info/frostfs/sdk/services/SessionClient.java @@ -1,7 +1,9 @@ package info.frostfs.sdk.services; import info.frostfs.sdk.dto.session.SessionToken; +import info.frostfs.sdk.jdo.parameters.CallContext; +import info.frostfs.sdk.jdo.parameters.session.PrmSessionCreate; public interface SessionClient { - SessionToken createSession(long expiration); + SessionToken createSession(PrmSessionCreate args, CallContext ctx); } diff --git a/client/src/main/java/info/frostfs/sdk/services/SessionTools.java b/client/src/main/java/info/frostfs/sdk/services/SessionTools.java index 7976592..b389235 100644 --- a/client/src/main/java/info/frostfs/sdk/services/SessionTools.java +++ b/client/src/main/java/info/frostfs/sdk/services/SessionTools.java @@ -1,9 +1,9 @@ package info.frostfs.sdk.services; -import frostfs.session.Types; import info.frostfs.sdk.dto.session.SessionToken; import info.frostfs.sdk.jdo.ClientEnvironment; +import info.frostfs.sdk.jdo.parameters.CallContext; public interface SessionTools { - Types.SessionToken getOrCreateSession(SessionToken token, ClientEnvironment env); + SessionToken getOrCreateSession(ClientEnvironment env, CallContext ctx); } diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/AccountingClientImpl.java b/client/src/main/java/info/frostfs/sdk/services/impl/AccountingClientImpl.java index deab5c2..7aee62b 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/AccountingClientImpl.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/AccountingClientImpl.java @@ -4,6 +4,7 @@ import frostfs.accounting.AccountingServiceGrpc; import frostfs.accounting.Service; import frostfs.accounting.Types; import info.frostfs.sdk.jdo.ClientEnvironment; +import info.frostfs.sdk.jdo.parameters.CallContext; import info.frostfs.sdk.mappers.object.OwnerIdMapper; import info.frostfs.sdk.services.AccountingClient; import info.frostfs.sdk.services.ContextAccessor; @@ -11,7 +12,7 @@ import info.frostfs.sdk.tools.RequestConstructor; import info.frostfs.sdk.tools.RequestSigner; import info.frostfs.sdk.tools.Verifier; -import java.util.Map; +import static info.frostfs.sdk.utils.DeadLineUtil.deadLineAfter; public class AccountingClientImpl extends ContextAccessor implements AccountingClient { private final AccountingServiceGrpc.AccountingServiceBlockingStub serviceBlockingStub; @@ -22,23 +23,24 @@ public class AccountingClientImpl extends ContextAccessor implements AccountingC } @Override - public Types.Decimal getBalance() { - var request = createGetRequest(null); + public Types.Decimal getBalance(CallContext ctx) { + var request = createGetRequest(); - var response = serviceBlockingStub.balance(request); + var service = deadLineAfter(serviceBlockingStub, ctx.getTimeout(), ctx.getTimeUnit()); + var response = service.balance(request); Verifier.checkResponse(response); return response.getBody().getBalance(); } - private Service.BalanceRequest createGetRequest(Map xHeaders) { + private Service.BalanceRequest createGetRequest() { var body = Service.BalanceRequest.Body.newBuilder() .setOwnerId(OwnerIdMapper.toGrpcMessage(getContext().getOwnerId())) .build(); var request = Service.BalanceRequest.newBuilder() .setBody(body); - RequestConstructor.addMetaHeader(request, xHeaders); + RequestConstructor.addMetaHeader(request); RequestSigner.sign(request, getContext().getKey()); return request.build(); diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/ApeManagerClientImpl.java b/client/src/main/java/info/frostfs/sdk/services/impl/ApeManagerClientImpl.java index 0e5c9f1..7843d65 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/ApeManagerClientImpl.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/ApeManagerClientImpl.java @@ -5,9 +5,11 @@ import frostfs.ape.Types; import frostfs.apemanager.APEManagerServiceGrpc; import frostfs.apemanager.Service; import info.frostfs.sdk.dto.chain.Chain; -import info.frostfs.sdk.dto.chain.ChainTarget; -import info.frostfs.sdk.exceptions.ValidationFrostFSException; import info.frostfs.sdk.jdo.ClientEnvironment; +import info.frostfs.sdk.jdo.parameters.CallContext; +import info.frostfs.sdk.jdo.parameters.ape.PrmApeChainAdd; +import info.frostfs.sdk.jdo.parameters.ape.PrmApeChainList; +import info.frostfs.sdk.jdo.parameters.ape.PrmApeChainRemove; import info.frostfs.sdk.mappers.chain.ChainMapper; import info.frostfs.sdk.mappers.chain.ChainTargetMapper; import info.frostfs.sdk.services.ApeManagerClient; @@ -17,10 +19,9 @@ import info.frostfs.sdk.tools.RequestSigner; import info.frostfs.sdk.tools.Verifier; import java.util.List; -import java.util.Map; -import static info.frostfs.sdk.constants.ErrorConst.*; -import static java.util.Objects.isNull; +import static info.frostfs.sdk.utils.DeadLineUtil.deadLineAfter; +import static info.frostfs.sdk.utils.Validator.validate; public class ApeManagerClientImpl extends ContextAccessor implements ApeManagerClient { private final APEManagerServiceGrpc.APEManagerServiceBlockingStub apeManagerServiceClient; @@ -31,19 +32,13 @@ public class ApeManagerClientImpl extends ContextAccessor implements ApeManagerC } @Override - public byte[] addChain(Chain chain, ChainTarget chainTarget) { - if (isNull(chain) || isNull(chainTarget)) { - throw new ValidationFrostFSException( - String.format( - PARAMS_ARE_MISSING_TEMPLATE, - String.join(FIELDS_DELIMITER_COMMA, Chain.class.getName(), ChainTarget.class.getName()) - ) - ); - } + public byte[] addChain(PrmApeChainAdd args, CallContext ctx) { + validate(args); - var request = createAddChainRequest(chain, chainTarget, null); + var request = createAddChainRequest(args); - var response = apeManagerServiceClient.addChain(request); + var service = deadLineAfter(apeManagerServiceClient, ctx.getTimeout(), ctx.getTimeUnit()); + var response = service.addChain(request); Verifier.checkResponse(response); @@ -51,82 +46,70 @@ public class ApeManagerClientImpl extends ContextAccessor implements ApeManagerC } @Override - public void removeChain(Chain chain, ChainTarget chainTarget) { - if (isNull(chain) || isNull(chainTarget)) { - throw new ValidationFrostFSException( - String.format( - PARAMS_ARE_MISSING_TEMPLATE, - String.join(FIELDS_DELIMITER_COMMA, Chain.class.getName(), ChainTarget.class.getName()) - ) - ); - } + public void removeChain(PrmApeChainRemove args, CallContext ctx) { + validate(args); - var request = createRemoveChainRequest(chain, chainTarget, null); + var request = createRemoveChainRequest(args); - var response = apeManagerServiceClient.removeChain(request); + var service = deadLineAfter(apeManagerServiceClient, ctx.getTimeout(), ctx.getTimeUnit()); + var response = service.removeChain(request); Verifier.checkResponse(response); } @Override - public List listChains(ChainTarget chainTarget) { - if (isNull(chainTarget)) { - throw new ValidationFrostFSException(String.format(PARAM_IS_MISSING_TEMPLATE, ChainTarget.class.getName())); - } + public List listChains(PrmApeChainList args, CallContext ctx) { + validate(args); - var request = createListChainsRequest(chainTarget, null); + var request = createListChainsRequest(args); - var response = apeManagerServiceClient.listChains(request); + var service = deadLineAfter(apeManagerServiceClient, ctx.getTimeout(), ctx.getTimeUnit()); + var response = service.listChains(request); Verifier.checkResponse(response); return ChainMapper.toModels(response.getBody().getChainsList()); } - private Service.AddChainRequest createAddChainRequest(Chain chain, - ChainTarget chainTarget, - Map xHeaders) { + private Service.AddChainRequest createAddChainRequest(PrmApeChainAdd args) { var chainGrpc = Types.Chain.newBuilder() - .setRaw(ByteString.copyFrom(chain.getRaw())) + .setRaw(ByteString.copyFrom(args.getChain().getRaw())) .build(); var body = Service.AddChainRequest.Body.newBuilder() .setChain(chainGrpc) - .setTarget(ChainTargetMapper.toGrpcMessage(chainTarget)) + .setTarget(ChainTargetMapper.toGrpcMessage(args.getChainTarget())) .build(); var request = Service.AddChainRequest.newBuilder() .setBody(body); - RequestConstructor.addMetaHeader(request, xHeaders); + RequestConstructor.addMetaHeader(request, args.getXHeaders()); RequestSigner.sign(request, getContext().getKey()); return request.build(); } - private Service.RemoveChainRequest createRemoveChainRequest(Chain chain, - ChainTarget chainTarget, - Map xHeaders) { + private Service.RemoveChainRequest createRemoveChainRequest(PrmApeChainRemove args) { var body = Service.RemoveChainRequest.Body.newBuilder() - .setChainId(ByteString.copyFrom(chain.getRaw())) - .setTarget(ChainTargetMapper.toGrpcMessage(chainTarget)) + .setChainId(ByteString.copyFrom(args.getChain().getRaw())) + .setTarget(ChainTargetMapper.toGrpcMessage(args.getChainTarget())) .build(); var request = Service.RemoveChainRequest.newBuilder() .setBody(body); - RequestConstructor.addMetaHeader(request, xHeaders); + RequestConstructor.addMetaHeader(request, args.getXHeaders()); RequestSigner.sign(request, getContext().getKey()); return request.build(); } - private Service.ListChainsRequest createListChainsRequest(ChainTarget chainTarget, - Map xHeaders) { + private Service.ListChainsRequest createListChainsRequest(PrmApeChainList args) { var body = Service.ListChainsRequest.Body.newBuilder() - .setTarget(ChainTargetMapper.toGrpcMessage(chainTarget)) + .setTarget(ChainTargetMapper.toGrpcMessage(args.getChainTarget())) .build(); var request = Service.ListChainsRequest.newBuilder() .setBody(body); - RequestConstructor.addMetaHeader(request, xHeaders); + RequestConstructor.addMetaHeader(request, args.getXHeaders()); RequestSigner.sign(request, getContext().getKey()); return request.build(); diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/ContainerClientImpl.java b/client/src/main/java/info/frostfs/sdk/services/impl/ContainerClientImpl.java index 77468af..e6c5d31 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/ContainerClientImpl.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/ContainerClientImpl.java @@ -10,9 +10,14 @@ import info.frostfs.sdk.enums.StatusCode; import info.frostfs.sdk.enums.WaitExpects; import info.frostfs.sdk.exceptions.ResponseFrostFSException; import info.frostfs.sdk.exceptions.TimeoutFrostFSException; -import info.frostfs.sdk.exceptions.ValidationFrostFSException; import info.frostfs.sdk.jdo.ClientEnvironment; -import info.frostfs.sdk.jdo.WaitParameters; +import info.frostfs.sdk.jdo.parameters.CallContext; +import info.frostfs.sdk.jdo.parameters.PrmWait; +import info.frostfs.sdk.jdo.parameters.container.PrmContainerCreate; +import info.frostfs.sdk.jdo.parameters.container.PrmContainerDelete; +import info.frostfs.sdk.jdo.parameters.container.PrmContainerGet; +import info.frostfs.sdk.jdo.parameters.container.PrmContainerGetAll; +import info.frostfs.sdk.jdo.parameters.session.SessionContext; import info.frostfs.sdk.mappers.container.ContainerIdMapper; import info.frostfs.sdk.mappers.container.ContainerMapper; import info.frostfs.sdk.mappers.netmap.VersionMapper; @@ -29,8 +34,11 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import static info.frostfs.sdk.constants.ErrorConst.PARAM_IS_MISSING_TEMPLATE; +import static info.frostfs.sdk.constants.AttributeConst.DISABLE_HOMOMORPHIC_HASHING_ATTRIBUTE; +import static info.frostfs.sdk.utils.DeadLineUtil.deadLineAfter; +import static info.frostfs.sdk.utils.Validator.validate; import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; public class ContainerClientImpl extends ContextAccessor implements ContainerClient { private final ContainerServiceGrpc.ContainerServiceBlockingStub serviceBlockingStub; @@ -42,29 +50,33 @@ public class ContainerClientImpl extends ContextAccessor implements ContainerCli this.sessionTools = new SessionToolsImpl(clientEnvironment); } - public frostfs.session.Types.SessionToken getOrCreateSession(SessionToken sessionToken) { - return sessionTools.getOrCreateSession(sessionToken, getContext()); + public SessionToken getOrCreateSession(SessionContext sessionContext, CallContext ctx) { + return isNull(sessionContext.getSessionToken()) + ? sessionTools.getOrCreateSession(getContext(), ctx) + : sessionContext.getSessionToken(); } @Override - public Container getContainer(ContainerId cid) { - if (isNull(cid)) { - throw new ValidationFrostFSException(String.format(PARAM_IS_MISSING_TEMPLATE, ContainerId.class.getName())); - } + public Container getContainer(PrmContainerGet args, CallContext ctx) { + validate(args); - var request = createGetRequest(ContainerIdMapper.toGrpcMessage(cid), null); + var request = createGetRequest(args); - var response = serviceBlockingStub.get(request); + var service = deadLineAfter(serviceBlockingStub, ctx.getTimeout(), ctx.getTimeUnit()); + var response = service.get(request); Verifier.checkResponse(response); return ContainerMapper.toModel(response.getBody().getContainer()); } @Override - public List listContainers() { - var request = createListRequest(null); + public List listContainers(PrmContainerGetAll args, CallContext ctx) { + validate(args); - var response = serviceBlockingStub.list(request); + var request = createListRequest(args); + + var service = deadLineAfter(serviceBlockingStub, ctx.getTimeout(), ctx.getTimeUnit()); + var response = service.list(request); Verifier.checkResponse(response); @@ -74,43 +86,39 @@ public class ContainerClientImpl extends ContextAccessor implements ContainerCli } @Override - public ContainerId createContainer(Container container) { - if (isNull(container)) { - throw new ValidationFrostFSException(String.format(PARAM_IS_MISSING_TEMPLATE, Container.class.getName())); - } + public ContainerId createContainer(PrmContainerCreate args, CallContext ctx) { + validate(args); - var grpcContainer = ContainerMapper.toGrpcMessage(container); - var request = createPutRequest(grpcContainer, null); + var request = createPutRequest(args, ctx); - var response = serviceBlockingStub.put(request); + var service = deadLineAfter(serviceBlockingStub, ctx.getTimeout(), ctx.getTimeUnit()); + var response = service.put(request); Verifier.checkResponse(response); - waitForContainer(WaitExpects.EXISTS, response.getBody().getContainerId(), null); + waitForContainer(WaitExpects.EXISTS, response.getBody().getContainerId(), args.getWaitParams()); return new ContainerId(response.getBody().getContainerId().getValue().toByteArray()); } @Override - public void deleteContainer(ContainerId cid) { - if (isNull(cid)) { - throw new ValidationFrostFSException(String.format(PARAM_IS_MISSING_TEMPLATE, ContainerId.class.getName())); - } + public void deleteContainer(PrmContainerDelete args, CallContext ctx) { + validate(args); - var grpcContainerId = ContainerIdMapper.toGrpcMessage(cid); - var request = createDeleteRequest(grpcContainerId, null); + var request = createDeleteRequest(args, ctx); - var response = serviceBlockingStub.delete(request); + var service = deadLineAfter(serviceBlockingStub, ctx.getTimeout(), ctx.getTimeUnit()); + var response = service.delete(request); Verifier.checkResponse(response); - waitForContainer(WaitExpects.REMOVED, request.getBody().getContainerId(), null); + waitForContainer(WaitExpects.REMOVED, request.getBody().getContainerId(), args.getWaitParams()); } - private void waitForContainer(WaitExpects expect, Types.ContainerID id, WaitParameters waitParams) { - var request = createGetRequest(id, null); + private void waitForContainer(WaitExpects expect, Types.ContainerID cid, PrmWait waitParams) { + var request = createGetRequest(cid, null); - waitParams = isNull(waitParams) ? new WaitParameters() : waitParams; + waitParams = isNull(waitParams) ? new PrmWait() : waitParams; var deadLine = waitParams.getDeadline(); while (true) { @@ -145,9 +153,12 @@ public class ContainerClientImpl extends ContextAccessor implements ContainerCli } } + private Service.GetRequest createGetRequest(PrmContainerGet args) { + var cid = ContainerIdMapper.toGrpcMessage(args.getContainerId()); + return createGetRequest(cid, args.getXHeaders()); + } - private Service.GetRequest createGetRequest(Types.ContainerID cid, - Map xHeaders) { + private Service.GetRequest createGetRequest(Types.ContainerID cid, Map xHeaders) { var body = Service.GetRequest.Body.newBuilder() .setContainerId(cid) .build(); @@ -160,22 +171,22 @@ public class ContainerClientImpl extends ContextAccessor implements ContainerCli return request.build(); } - private Service.ListRequest createListRequest(Map xHeaders) { + private Service.ListRequest createListRequest(PrmContainerGetAll args) { var body = Service.ListRequest.Body.newBuilder() .setOwnerId(OwnerIdMapper.toGrpcMessage(getContext().getOwnerId())) .build(); var request = Service.ListRequest.newBuilder() .setBody(body); - RequestConstructor.addMetaHeader(request, xHeaders); + RequestConstructor.addMetaHeader(request, args.getXHeaders()); RequestSigner.sign(request, getContext().getKey()); return request.build(); } - private Service.PutRequest createPutRequest(frostfs.container.Types.Container container, - Map xHeaders) { - container = container.toBuilder() + private Service.PutRequest createPutRequest(PrmContainerCreate args, CallContext ctx) { + syncContainerWithNetwork(args.getContainer(), ctx); + var container = ContainerMapper.toGrpcMessage(args.getContainer()).toBuilder() .setOwnerId(OwnerIdMapper.toGrpcMessage(getContext().getOwnerId())) .setVersion(VersionMapper.toGrpcMessage(getContext().getVersion())) .build(); @@ -187,9 +198,8 @@ public class ContainerClientImpl extends ContextAccessor implements ContainerCli var request = Service.PutRequest.newBuilder() .setBody(body); - var sessionToken = getOrCreateSession(null); - - sessionToken = RequestConstructor.createContainerTokenContext( + var sessionToken = getOrCreateSession(args, ctx); + var protoToken = RequestConstructor.createContainerTokenContext( sessionToken, null, frostfs.session.Types.ContainerSessionContext.Verb.PUT, @@ -197,14 +207,15 @@ public class ContainerClientImpl extends ContextAccessor implements ContainerCli getContext().getKey() ); - RequestConstructor.addMetaHeader(request, xHeaders, sessionToken); + RequestConstructor.addMetaHeader(request, args.getXHeaders(), protoToken); RequestSigner.sign(request, getContext().getKey()); return request.build(); } - private Service.DeleteRequest createDeleteRequest(Types.ContainerID cid, - Map xHeaders) { + private Service.DeleteRequest createDeleteRequest(PrmContainerDelete args, CallContext ctx) { + var cid = ContainerIdMapper.toGrpcMessage(args.getContainerId()); + var body = Service.DeleteRequest.Body.newBuilder() .setContainerId(cid) .setSignature(RequestSigner.signRFC6979(getContext().getKey(), cid.getValue())) @@ -212,9 +223,8 @@ public class ContainerClientImpl extends ContextAccessor implements ContainerCli var request = Service.DeleteRequest.newBuilder() .setBody(body); - var sessionToken = getOrCreateSession(null); - - sessionToken = RequestConstructor.createContainerTokenContext( + var sessionToken = getOrCreateSession(args, ctx); + var protoToken = RequestConstructor.createContainerTokenContext( sessionToken, null, frostfs.session.Types.ContainerSessionContext.Verb.DELETE, @@ -222,9 +232,18 @@ public class ContainerClientImpl extends ContextAccessor implements ContainerCli getContext().getKey() ); - RequestConstructor.addMetaHeader(request, xHeaders, sessionToken); + RequestConstructor.addMetaHeader(request, args.getXHeaders(), protoToken); RequestSigner.sign(request, getContext().getKey()); return request.build(); } + + private void syncContainerWithNetwork(Container container, CallContext callContext) { + var settings = getContext().getFrostFSClient().getNetworkSettings(callContext); + if (nonNull(settings.getHomomorphicHashingDisabled()) && settings.getHomomorphicHashingDisabled()) { + container.getAttributes().put(DISABLE_HOMOMORPHIC_HASHING_ATTRIBUTE, Boolean.TRUE.toString()); + } else { + container.getAttributes().remove(DISABLE_HOMOMORPHIC_HASHING_ATTRIBUTE, Boolean.TRUE.toString()); + } + } } diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/NetmapClientImpl.java b/client/src/main/java/info/frostfs/sdk/services/impl/NetmapClientImpl.java index 04109ad..a32abac 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/NetmapClientImpl.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/NetmapClientImpl.java @@ -7,6 +7,7 @@ import info.frostfs.sdk.dto.netmap.NetmapSnapshot; import info.frostfs.sdk.dto.netmap.NodeInfo; import info.frostfs.sdk.jdo.ClientEnvironment; import info.frostfs.sdk.jdo.NetworkSettings; +import info.frostfs.sdk.jdo.parameters.CallContext; import info.frostfs.sdk.mappers.netmap.NetmapSnapshotMapper; import info.frostfs.sdk.mappers.netmap.NodeInfoMapper; import info.frostfs.sdk.services.ContextAccessor; @@ -17,6 +18,7 @@ import info.frostfs.sdk.tools.Verifier; import java.nio.charset.StandardCharsets; import static info.frostfs.sdk.tools.RequestSigner.sign; +import static info.frostfs.sdk.utils.DeadLineUtil.deadLineAfter; import static java.util.Objects.nonNull; public class NetmapClientImpl extends ContextAccessor implements NetmapClient { @@ -94,12 +96,12 @@ public class NetmapClientImpl extends ContextAccessor implements NetmapClient { } @Override - public NetworkSettings getNetworkSettings() { + public NetworkSettings getNetworkSettings(CallContext ctx) { if (nonNull(getContext().getNetworkSettings())) { return getContext().getNetworkSettings(); } - var info = getNetworkInfo(); + var info = getNetworkInfo(ctx); var settings = new NetworkSettings(); @@ -113,25 +115,28 @@ public class NetmapClientImpl extends ContextAccessor implements NetmapClient { } @Override - public NodeInfo getLocalNodeInfo() { + public NodeInfo getLocalNodeInfo(CallContext ctx) { var request = Service.LocalNodeInfoRequest.newBuilder(); RequestConstructor.addMetaHeader(request); sign(request, getContext().getKey()); - var response = netmapServiceClient.localNodeInfo(request.build()); + var service = deadLineAfter(netmapServiceClient, ctx.getTimeout(), ctx.getTimeUnit()); + var response = service.localNodeInfo(request.build()); + Verifier.checkResponse(response); return NodeInfoMapper.toModel(response.getBody()); } - public Service.NetworkInfoResponse getNetworkInfo() { + public Service.NetworkInfoResponse getNetworkInfo(CallContext ctx) { var request = Service.NetworkInfoRequest.newBuilder(); RequestConstructor.addMetaHeader(request); sign(request, getContext().getKey()); - var response = netmapServiceClient.networkInfo(request.build()); + var service = deadLineAfter(netmapServiceClient, ctx.getTimeout(), ctx.getTimeUnit()); + var response = service.networkInfo(request.build()); Verifier.checkResponse(response); @@ -139,13 +144,14 @@ public class NetmapClientImpl extends ContextAccessor implements NetmapClient { } @Override - public NetmapSnapshot getNetmapSnapshot() { + public NetmapSnapshot getNetmapSnapshot(CallContext ctx) { var request = Service.NetmapSnapshotRequest.newBuilder(); RequestConstructor.addMetaHeader(request); sign(request, getContext().getKey()); - var response = netmapServiceClient.netmapSnapshot(request.build()); + var service = deadLineAfter(netmapServiceClient, ctx.getTimeout(), ctx.getTimeUnit()); + var response = service.netmapSnapshot(request.build()); Verifier.checkResponse(response); diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/ObjectClientImpl.java b/client/src/main/java/info/frostfs/sdk/services/impl/ObjectClientImpl.java index f85936c..203d1e6 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/ObjectClientImpl.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/ObjectClientImpl.java @@ -6,29 +6,26 @@ import frostfs.object.ObjectServiceGrpc; import frostfs.object.Service; import frostfs.refs.Types; import info.frostfs.sdk.constants.AppConst; -import info.frostfs.sdk.dto.container.ContainerId; import info.frostfs.sdk.dto.object.*; import info.frostfs.sdk.dto.session.SessionToken; import info.frostfs.sdk.enums.ObjectType; import info.frostfs.sdk.exceptions.ProcessFrostFSException; -import info.frostfs.sdk.exceptions.ValidationFrostFSException; import info.frostfs.sdk.jdo.ClientEnvironment; -import info.frostfs.sdk.jdo.PutObjectParameters; import info.frostfs.sdk.jdo.PutObjectResult; +import info.frostfs.sdk.jdo.parameters.CallContext; +import info.frostfs.sdk.jdo.parameters.object.*; +import info.frostfs.sdk.jdo.parameters.session.SessionContext; +import info.frostfs.sdk.jdo.result.ObjectHeaderResult; import info.frostfs.sdk.mappers.container.ContainerIdMapper; -import info.frostfs.sdk.mappers.object.ObjectFilterMapper; -import info.frostfs.sdk.mappers.object.ObjectFrostFSMapper; -import info.frostfs.sdk.mappers.object.ObjectHeaderMapper; -import info.frostfs.sdk.mappers.object.ObjectIdMapper; -import info.frostfs.sdk.mappers.session.SessionMapper; +import info.frostfs.sdk.mappers.object.*; import info.frostfs.sdk.services.ContextAccessor; import info.frostfs.sdk.services.ObjectClient; import info.frostfs.sdk.services.impl.rwhelper.ObjectReaderImpl; +import info.frostfs.sdk.services.impl.rwhelper.ObjectStreamer; import info.frostfs.sdk.services.impl.rwhelper.ObjectWriter; import info.frostfs.sdk.services.impl.rwhelper.SearchReader; import info.frostfs.sdk.tools.RequestConstructor; import info.frostfs.sdk.tools.Verifier; -import info.frostfs.sdk.utils.Validator; import org.apache.commons.collections4.CollectionUtils; import java.io.IOException; @@ -37,9 +34,12 @@ import java.util.ArrayList; import java.util.List; import static info.frostfs.sdk.Helper.getSha256; -import static info.frostfs.sdk.constants.ErrorConst.*; +import static info.frostfs.sdk.constants.ErrorConst.PROTO_MESSAGE_IS_EMPTY_TEMPLATE; import static info.frostfs.sdk.tools.RequestSigner.sign; +import static info.frostfs.sdk.utils.DeadLineUtil.deadLineAfter; +import static info.frostfs.sdk.utils.Validator.validate; import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; public class ObjectClientImpl extends ContextAccessor implements ObjectClient { private final ObjectServiceGrpc.ObjectServiceBlockingStub objectServiceBlockingClient; @@ -55,87 +55,153 @@ public class ObjectClientImpl extends ContextAccessor implements ObjectClient { this.sessionTools = new SessionToolsImpl(clientEnvironment); } - public frostfs.session.Types.SessionToken getOrCreateSession(SessionToken sessionToken) { - return sessionTools.getOrCreateSession(sessionToken, getContext()); + public SessionToken getOrCreateSession(SessionContext sessionContext, CallContext ctx) { + return isNull(sessionContext.getSessionToken()) + ? sessionTools.getOrCreateSession(getContext(), ctx) + : sessionContext.getSessionToken(); } @Override - public ObjectHeader getObjectHead(ContainerId cid, ObjectId oid) { - if (isNull(cid) || isNull(oid)) { - throw new ValidationFrostFSException( - String.format( - PARAMS_ARE_MISSING_TEMPLATE, - String.join(FIELDS_DELIMITER_COMMA, ContainerId.class.getName(), ObjectId.class.getName()) - ) - ); - } + public ObjectHeaderResult getObjectHead(PrmObjectHeadGet args, CallContext ctx) { + validate(args); - var request = createHeadRequest(ContainerIdMapper.toGrpcMessage(cid), ObjectIdMapper.toGrpcMessage(oid)); + var request = createHeadRequest(args, ctx); - var response = objectServiceBlockingClient.head(request); + var service = deadLineAfter(objectServiceBlockingClient, ctx.getTimeout(), ctx.getTimeUnit()); + var response = service.head(request); Verifier.checkResponse(response); - return ObjectHeaderMapper.toModel(response.getBody().getHeader().getHeader()); + return ObjectHeaderResult.builder() + .headerInfo(ObjectHeaderMapper.toModel(response.getBody().getHeader().getHeader())) + .splitInfo(SplitInfoMapper.toModel(response.getBody().getSplitInfo())) + .build(); } @Override - public ObjectFrostFS getObject(ContainerId cid, ObjectId oid) { - var request = createGetRequest(ContainerIdMapper.toGrpcMessage(cid), ObjectIdMapper.toGrpcMessage(oid)); + public ObjectFrostFS getObject(PrmObjectGet args, CallContext ctx) { + validate(args); - return getObject(request); + var request = createGetRequest(args, ctx); + + return getObject(request, ctx); } @Override - public void deleteObject(ContainerId cid, ObjectId oid) { - var request = createDeleteRequest(ContainerIdMapper.toGrpcMessage(cid), ObjectIdMapper.toGrpcMessage(oid)); + public void deleteObject(PrmObjectDelete args, CallContext ctx) { + validate(args); - var response = objectServiceBlockingClient.delete(request); + var request = createDeleteRequest(args, ctx); + + var service = deadLineAfter(objectServiceBlockingClient, ctx.getTimeout(), ctx.getTimeUnit()); + var response = service.delete(request); Verifier.checkResponse(response); } @Override - public Iterable searchObjects(ContainerId cid, ObjectFilter... filters) { - var request = createSearchRequest(ContainerIdMapper.toGrpcMessage(cid), filters); + public Iterable searchObjects(PrmObjectSearch args, CallContext ctx) { + validate(args); - var objectsIds = searchObjects(request); + var request = createSearchRequest(args, ctx); + + var objectsIds = searchObjects(request, ctx); return Iterables.transform(objectsIds, input -> new ObjectId(input.getValue().toByteArray())); } @Override - public ObjectId putObject(PutObjectParameters parameters) { - Validator.validate(parameters); + public ObjectWriter putObject(PrmObjectPut args, CallContext ctx) { + validate(args); - if (parameters.isClientCut()) { - return putClientCutObject(parameters); - } - - if (parameters.getHeader().getPayloadLength() > 0) { - parameters.setFullLength(parameters.getHeader().getPayloadLength()); - } else { - parameters.setFullLength(getStreamSize(parameters.getPayload())); - } - - return putStreamObject(parameters).getObjectId(); + return new ObjectWriter(getContext(), args, getUploadStream(args, ctx)); } @Override - public ObjectId putSingleObject(ObjectFrostFS modelObject) { - var grpcObject = objectToolsImpl.createObject(modelObject); + public ObjectId putClientCutObject(PrmObjectClientCutPut args, CallContext ctx) { + validate(args); - var request = createPutSingleRequest(grpcObject); + var header = args.getObjectHeader(); + var fullLength = header.getPayloadLength() == 0 ? getStreamSize(args.getPayload()) : header.getPayloadLength(); + args.getPutObjectContext().setFullLength(fullLength); - var response = objectServiceBlockingClient.putSingle(request); + if (args.getPutObjectContext().getMaxObjectSizeCache() == 0) { + var networkSettings = getContext().getFrostFSClient().getNetworkSettings(ctx); + args.getPutObjectContext().setMaxObjectSizeCache(networkSettings.getMaxObjectSize().intValue()); + } + + var restBytes = fullLength - args.getPutObjectContext().getCurrentStreamPosition(); + var objectSize = restBytes > 0 + ? Math.min(args.getPutObjectContext().getMaxObjectSizeCache(), restBytes) + : args.getPutObjectContext().getMaxObjectSizeCache(); + + //define collection capacity + var restPart = (restBytes % objectSize) > 0 ? 1 : 0; + var objectsCount = fullLength > 0 ? (int) (restBytes / objectSize) + restPart : 0; + + List sentObjectIds = new ArrayList<>(objectsCount); + + // keep attributes for the large object + var attributes = args.getObjectHeader().getAttributes(); + + Split split = new Split(); + args.getObjectHeader().setAttributes(new ArrayList<>()); + + // send all parts except the last one as separate Objects + while (restBytes > (long) args.getPutObjectContext().getMaxObjectSizeCache()) { + var previous = CollectionUtils.isNotEmpty(sentObjectIds) + ? sentObjectIds.get(sentObjectIds.size() - 1) + : null; + split.setPrevious(previous); + args.getObjectHeader().setSplit(split); + + var result = putMultipartStreamObject(args, ctx); + + sentObjectIds.add(result.getObjectId()); + + restBytes -= result.getObjectSize(); + } + + // send the last part and create linkObject + if (CollectionUtils.isNotEmpty(sentObjectIds)) { + var largeObjectHeader = + new ObjectHeader(header.getContainerId(), ObjectType.REGULAR, attributes, fullLength, null); + + split.setParentHeader(largeObjectHeader); + + var result = putMultipartStreamObject(args, ctx); + + sentObjectIds.add(result.getObjectId()); + + var linkObject = new LinkObject(header.getContainerId(), split.getSplitId(), largeObjectHeader); + linkObject.addChildren(sentObjectIds); + + putSingleObject(new PrmObjectSinglePut(linkObject), ctx); + + return split.getParent(); + } + + // We are here if the payload is placed to one Object. It means no cut action, just simple PUT. + var singlePartResult = putMultipartStreamObject(args, ctx); + + return singlePartResult.getObjectId(); + } + + @Override + public ObjectId putSingleObject(PrmObjectSinglePut args, CallContext ctx) { + var grpcObject = objectToolsImpl.createObject(args.getObjectFrostFS()); + var request = createPutSingleRequest(grpcObject, args, ctx); + + var service = deadLineAfter(objectServiceBlockingClient, ctx.getTimeout(), ctx.getTimeUnit()); + var response = service.putSingle(request); Verifier.checkResponse(response); return new ObjectId(grpcObject.getObjectId().getValue().toByteArray()); } - private ObjectFrostFS getObject(Service.GetRequest request) { - var reader = getObjectInit(request); + private ObjectFrostFS getObject(Service.GetRequest request, CallContext ctx) { + var reader = getObjectInit(request, ctx); var grpcObject = reader.readHeader(); var modelObject = ObjectFrostFSMapper.toModel(grpcObject); @@ -145,39 +211,41 @@ public class ObjectClientImpl extends ContextAccessor implements ObjectClient { return modelObject; } - private ObjectReaderImpl getObjectInit(Service.GetRequest initRequest) { + private ObjectReaderImpl getObjectInit(Service.GetRequest initRequest, CallContext ctx) { if (initRequest.getSerializedSize() == 0) { throw new ProcessFrostFSException( String.format(PROTO_MESSAGE_IS_EMPTY_TEMPLATE, initRequest.getClass().getName()) ); } - return new ObjectReaderImpl(objectServiceBlockingClient.get(initRequest)); + var service = deadLineAfter(objectServiceBlockingClient, ctx.getTimeout(), ctx.getTimeUnit()); + return new ObjectReaderImpl(service.get(initRequest)); } - private PutObjectResult putStreamObject(PutObjectParameters parameters) { - var chunkSize = parameters.getBufferMaxSize() > 0 ? parameters.getBufferMaxSize() : AppConst.OBJECT_CHUNK_SIZE; + private PutObjectResult putMultipartStreamObject(PrmObjectClientCutPut args, CallContext ctx) { + var chunkSize = args.getBufferMaxSize() > 0 ? args.getBufferMaxSize() : AppConst.OBJECT_CHUNK_SIZE; - var restBytes = parameters.getFullLength() - parameters.getCurrentStreamPosition(); + var restBytes = + args.getPutObjectContext().getFullLength() - args.getPutObjectContext().getCurrentStreamPosition(); chunkSize = (int) Math.min(restBytes, chunkSize); - byte[] chunkBuffer = parameters.getCustomerBuffer() != null - ? parameters.getCustomerBuffer() + byte[] chunkBuffer = args.getCustomerBuffer() != null + ? args.getCustomerBuffer() : new byte[chunkSize];//todo change to pool var sentBytes = 0; // 0 means no limit from client, so server side cut is performed - var objectLimitSize = parameters.isClientCut() ? parameters.getMaxObjectSizeCache() : 0; + var objectLimitSize = args.getPutObjectContext().getMaxObjectSizeCache(); - var stream = getUploadStream(parameters); + var stream = getUploadStream(args, ctx); while (objectLimitSize == 0 || sentBytes < objectLimitSize) { // send chunks limited to default or user's settings var bufferSize = objectLimitSize > 0 ? Math.min(objectLimitSize - sentBytes, chunkSize) : chunkSize; - var bytesCount = readNBytes(parameters.getPayload(), chunkBuffer, bufferSize); + var bytesCount = readNBytes(args.getPayload(), chunkBuffer, bufferSize); if (bytesCount == 0) { break; @@ -204,110 +272,40 @@ public class ObjectClientImpl extends ContextAccessor implements ObjectClient { return new PutObjectResult(objectId, sentBytes); } - private ObjectId putClientCutObject(PutObjectParameters parameters) { - var header = parameters.getHeader(); - var tokenRaw = getOrCreateSession(parameters.getSessionToken()); - var token = new SessionToken(SessionMapper.serialize(tokenRaw)); - parameters.setSessionToken(token); - - var fullLength = header.getPayloadLength() == 0 - ? getStreamSize(parameters.getPayload()) - : header.getPayloadLength(); - - parameters.setFullLength(fullLength); - - if (parameters.getMaxObjectSizeCache() == 0) { - var networkSettings = getContext().getFrostFSClient().getNetworkSettings(); - parameters.setMaxObjectSizeCache(networkSettings.getMaxObjectSize().intValue()); - } - - var restBytes = fullLength - parameters.getCurrentStreamPosition(); - var objectSize = restBytes > 0 - ? Math.min(parameters.getMaxObjectSizeCache(), restBytes) - : parameters.getMaxObjectSizeCache(); - - //define collection capacity - var restPart = (restBytes % objectSize) > 0 ? 1 : 0; - var objectsCount = fullLength > 0 ? (int) (restBytes / objectSize) + restPart : 0; - - List sentObjectIds = new ArrayList<>(objectsCount); - - // keep attributes for the large object - var attributes = parameters.getHeader().getAttributes(); - - Split split = new Split(); - parameters.getHeader().setSplit(split); - parameters.getHeader().setAttributes(new ArrayList<>()); - - // send all parts except the last one as separate Objects - while (restBytes > (long) parameters.getMaxObjectSizeCache()) { - var previous = CollectionUtils.isNotEmpty(sentObjectIds) - ? sentObjectIds.get(sentObjectIds.size() - 1) - : null; - split.setPrevious(previous); - - var result = putStreamObject(parameters); - - sentObjectIds.add(result.getObjectId()); - - restBytes -= result.getObjectSize(); - } - - // send the last part and create linkObject - if (CollectionUtils.isNotEmpty(sentObjectIds)) { - var largeObjectHeader = - new ObjectHeader(header.getContainerId(), ObjectType.REGULAR, attributes, fullLength, null); - - split.setParentHeader(largeObjectHeader); - - var result = putStreamObject(parameters); - - sentObjectIds.add(result.getObjectId()); - - var linkObject = new LinkObject(header.getContainerId(), split.getSplitId(), largeObjectHeader); - linkObject.addChildren(sentObjectIds); - - putSingleObject(linkObject); - - return split.getParent(); - } - - // We are here if the payload is placed to one Object. It means no cut action, just simple PUT. - var singlePartResult = putStreamObject(parameters); - - return singlePartResult.getObjectId(); - } - - private ObjectWriter getUploadStream(PutObjectParameters parameters) { - var header = parameters.getHeader(); + private ObjectStreamer getUploadStream(PrmObjectPutBase args, CallContext ctx) { + var header = args.getObjectHeader(); header.setOwnerId(getContext().getOwnerId()); header.setVersion(getContext().getVersion()); var grpcHeader = ObjectHeaderMapper.toGrpcMessage(header); - grpcHeader = objectToolsImpl.updateSplitValues(grpcHeader, header.getSplit()); + + if (nonNull(header.getSplit())) { + grpcHeader = objectToolsImpl.updateSplitValues(grpcHeader, header.getSplit()); + } var oid = Types.ObjectID.newBuilder().setValue(getSha256(grpcHeader)).build(); - var initRequest = createInitPutRequest(oid, grpcHeader); + var initRequest = createInitPutRequest(oid, grpcHeader, args, ctx); - return putObjectInit(initRequest); + return putObjectInit(initRequest, ctx); } - private ObjectWriter putObjectInit(Service.PutRequest initRequest) { + private ObjectStreamer putObjectInit(Service.PutRequest initRequest, CallContext ctx) { if (initRequest.getSerializedSize() == 0) { throw new ProcessFrostFSException( String.format(PROTO_MESSAGE_IS_EMPTY_TEMPLATE, initRequest.getClass().getName()) ); } - ObjectWriter writer = new ObjectWriter(objectServiceClient); + var service = deadLineAfter(objectServiceClient, ctx.getTimeout(), ctx.getTimeUnit()); + ObjectStreamer writer = new ObjectStreamer(service); writer.write(initRequest); return writer; } - private Iterable searchObjects(Service.SearchRequest request) { - var reader = getSearchReader(request); + private Iterable searchObjects(Service.SearchRequest request, CallContext ctx) { + var reader = getSearchReader(request, ctx); var ids = reader.read(); List result = new ArrayList<>(); @@ -319,14 +317,15 @@ public class ObjectClientImpl extends ContextAccessor implements ObjectClient { return result;//todo return yield } - private SearchReader getSearchReader(Service.SearchRequest initRequest) { + private SearchReader getSearchReader(Service.SearchRequest initRequest, CallContext ctx) { if (initRequest.getSerializedSize() == 0) { throw new ProcessFrostFSException( String.format(PROTO_MESSAGE_IS_EMPTY_TEMPLATE, initRequest.getClass().getName()) ); } - return new SearchReader(objectServiceBlockingClient.search(initRequest)); + var service = deadLineAfter(objectServiceBlockingClient, ctx.getTimeout(), ctx.getTimeUnit()); + return new SearchReader(service.search(initRequest)); } private int readNBytes(InputStream inputStream, byte[] buffer, int size) { @@ -345,36 +344,37 @@ public class ObjectClientImpl extends ContextAccessor implements ObjectClient { } } - private Service.HeadRequest createHeadRequest(Types.ContainerID cid, Types.ObjectID oid) { + private Service.HeadRequest createHeadRequest(PrmObjectHeadGet args, CallContext ctx) { var address = Types.Address.newBuilder() - .setContainerId(cid) - .setObjectId(oid) + .setContainerId(ContainerIdMapper.toGrpcMessage(args.getContainerId())) + .setObjectId(ObjectIdMapper.toGrpcMessage(args.getObjectId())) .build(); var body = Service.HeadRequest.Body.newBuilder() .setAddress(address) + .setRaw(args.isRaw()) .build(); var request = Service.HeadRequest.newBuilder() .setBody(body); - var sessionToken = getOrCreateSession(null); - - sessionToken = RequestConstructor.createObjectTokenContext( + var sessionToken = getOrCreateSession(args, ctx); + var protoToken = RequestConstructor.createObjectTokenContext( sessionToken, address, frostfs.session.Types.ObjectSessionContext.Verb.HEAD, getContext().getKey() ); - RequestConstructor.addMetaHeader(request, null, sessionToken); + RequestConstructor.addMetaHeader(request, args.getXHeaders(), protoToken); sign(request, getContext().getKey()); return request.build(); } - private Service.GetRequest createGetRequest(Types.ContainerID cid, Types.ObjectID oid) { + private Service.GetRequest createGetRequest(PrmObjectGet args, CallContext ctx) { + var address = Types.Address.newBuilder() - .setContainerId(cid) - .setObjectId(oid) + .setContainerId(ContainerIdMapper.toGrpcMessage(args.getContainerId())) + .setObjectId(ObjectIdMapper.toGrpcMessage(args.getObjectId())) .build(); var body = Service.GetRequest.Body.newBuilder() .setAddress(address) @@ -382,25 +382,24 @@ public class ObjectClientImpl extends ContextAccessor implements ObjectClient { var request = Service.GetRequest.newBuilder() .setBody(body); - var sessionToken = getOrCreateSession(null); - - sessionToken = RequestConstructor.createObjectTokenContext( + var sessionToken = getOrCreateSession(args, ctx); + var protoToken = RequestConstructor.createObjectTokenContext( sessionToken, address, frostfs.session.Types.ObjectSessionContext.Verb.GET, getContext().getKey() ); - RequestConstructor.addMetaHeader(request, null, sessionToken); + RequestConstructor.addMetaHeader(request, args.getXHeaders(), protoToken); sign(request, getContext().getKey()); return request.build(); } - private Service.DeleteRequest createDeleteRequest(Types.ContainerID cid, Types.ObjectID oid) { + private Service.DeleteRequest createDeleteRequest(PrmObjectDelete args, CallContext ctx) { var address = Types.Address.newBuilder() - .setContainerId(cid) - .setObjectId(oid) + .setContainerId(ContainerIdMapper.toGrpcMessage(args.getContainerId())) + .setObjectId(ObjectIdMapper.toGrpcMessage(args.getObjectId())) .build(); var body = Service.DeleteRequest.Body.newBuilder() .setAddress(address) @@ -408,22 +407,23 @@ public class ObjectClientImpl extends ContextAccessor implements ObjectClient { var request = Service.DeleteRequest.newBuilder() .setBody(body); - var sessionToken = getOrCreateSession(null); - - sessionToken = RequestConstructor.createObjectTokenContext( + var sessionToken = getOrCreateSession(args, ctx); + var protoToken = RequestConstructor.createObjectTokenContext( sessionToken, address, frostfs.session.Types.ObjectSessionContext.Verb.DELETE, getContext().getKey() ); - RequestConstructor.addMetaHeader(request, null, sessionToken); + RequestConstructor.addMetaHeader(request, args.getXHeaders(), protoToken); sign(request, getContext().getKey()); return request.build(); } - private Service.SearchRequest createSearchRequest(Types.ContainerID cid, ObjectFilter... filters) { + private Service.SearchRequest createSearchRequest(PrmObjectSearch args, CallContext ctx) { + var cid = ContainerIdMapper.toGrpcMessage(args.getContainerId()); + var address = Types.Address.newBuilder() .setContainerId(cid) .build(); @@ -432,29 +432,31 @@ public class ObjectClientImpl extends ContextAccessor implements ObjectClient { .setContainerId(cid) .setVersion(1);// TODO: clarify this param - for (ObjectFilter filter : filters) { + for (ObjectFilter filter : args.getFilters()) { body.addFilters(ObjectFilterMapper.toGrpcMessage(filter)); } var request = Service.SearchRequest.newBuilder() .setBody(body.build()); - var sessionToken = getOrCreateSession(null); - - sessionToken = RequestConstructor.createObjectTokenContext( + var sessionToken = getOrCreateSession(args, ctx); + var protoToken = RequestConstructor.createObjectTokenContext( sessionToken, address, frostfs.session.Types.ObjectSessionContext.Verb.SEARCH, getContext().getKey() ); - RequestConstructor.addMetaHeader(request, null, sessionToken); + RequestConstructor.addMetaHeader(request, args.getXHeaders(), protoToken); sign(request, getContext().getKey()); return request.build(); } - private Service.PutRequest createInitPutRequest(Types.ObjectID oid, frostfs.object.Types.Header header) { + private Service.PutRequest createInitPutRequest(Types.ObjectID oid, + frostfs.object.Types.Header header, + PrmObjectPutBase args, + CallContext ctx) { var address = Types.Address.newBuilder() .setContainerId(header.getContainerId()) .setObjectId(oid) @@ -468,22 +470,23 @@ public class ObjectClientImpl extends ContextAccessor implements ObjectClient { var request = Service.PutRequest.newBuilder() .setBody(body); - var sessionToken = getOrCreateSession(null); - - sessionToken = RequestConstructor.createObjectTokenContext( + var sessionToken = getOrCreateSession(args, ctx); + var protoToken = RequestConstructor.createObjectTokenContext( sessionToken, address, frostfs.session.Types.ObjectSessionContext.Verb.PUT, getContext().getKey() ); - RequestConstructor.addMetaHeader(request, null, sessionToken); + RequestConstructor.addMetaHeader(request, args.getXHeaders(), protoToken); sign(request, getContext().getKey()); return request.build(); } - private Service.PutSingleRequest createPutSingleRequest(frostfs.object.Types.Object grpcObject) { + private Service.PutSingleRequest createPutSingleRequest(frostfs.object.Types.Object grpcObject, + PrmObjectSinglePut args, + CallContext ctx) { var address = Types.Address.newBuilder() .setContainerId(grpcObject.getHeader().getContainerId()) .setObjectId(grpcObject.getObjectId()) @@ -494,16 +497,15 @@ public class ObjectClientImpl extends ContextAccessor implements ObjectClient { var request = Service.PutSingleRequest.newBuilder() .setBody(body); - var sessionToken = getOrCreateSession(null); - - sessionToken = RequestConstructor.createObjectTokenContext( + var sessionToken = getOrCreateSession(args, ctx); + var protoToken = RequestConstructor.createObjectTokenContext( sessionToken, address, frostfs.session.Types.ObjectSessionContext.Verb.PUT, getContext().getKey() ); - RequestConstructor.addMetaHeader(request, null, sessionToken); + RequestConstructor.addMetaHeader(request, args.getXHeaders(), protoToken); sign(request, getContext().getKey()); return request.build(); diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/SessionClientImpl.java b/client/src/main/java/info/frostfs/sdk/services/impl/SessionClientImpl.java index 8151fc9..7b999ce 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/SessionClientImpl.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/SessionClientImpl.java @@ -5,6 +5,8 @@ import frostfs.session.SessionServiceGrpc; import frostfs.session.Types; import info.frostfs.sdk.dto.session.SessionToken; import info.frostfs.sdk.jdo.ClientEnvironment; +import info.frostfs.sdk.jdo.parameters.CallContext; +import info.frostfs.sdk.jdo.parameters.session.PrmSessionCreate; import info.frostfs.sdk.mappers.object.OwnerIdMapper; import info.frostfs.sdk.mappers.session.SessionMapper; import info.frostfs.sdk.services.ContextAccessor; @@ -13,6 +15,7 @@ import info.frostfs.sdk.tools.RequestConstructor; import info.frostfs.sdk.tools.Verifier; import static info.frostfs.sdk.tools.RequestSigner.sign; +import static info.frostfs.sdk.utils.DeadLineUtil.deadLineAfter; public class SessionClientImpl extends ContextAccessor implements SessionClient { private final SessionServiceGrpc.SessionServiceBlockingStub serviceBlockingStub; @@ -23,16 +26,16 @@ public class SessionClientImpl extends ContextAccessor implements SessionClient } @Override - public SessionToken createSession(long expiration) { - var sessionToken = createSessionInternal(expiration); + public SessionToken createSession(PrmSessionCreate args, CallContext ctx) { + var sessionToken = createSessionInternal(args, ctx); var token = SessionMapper.serialize(sessionToken); return new SessionToken(token); } - public Types.SessionToken createSessionInternal(long expiration) { + public Types.SessionToken createSessionInternal(PrmSessionCreate args, CallContext ctx) { var body = Service.CreateRequest.Body.newBuilder() .setOwnerId(OwnerIdMapper.toGrpcMessage(getContext().getOwnerId())) - .setExpiration(expiration) + .setExpiration(args.getExpiration()) .build(); var request = Service.CreateRequest.newBuilder() .setBody(body); @@ -40,11 +43,12 @@ public class SessionClientImpl extends ContextAccessor implements SessionClient RequestConstructor.addMetaHeader(request); sign(request, getContext().getKey()); - return createSession(request.build()); + return createSession(request.build(), ctx); } - private Types.SessionToken createSession(Service.CreateRequest request) { - var response = serviceBlockingStub.create(request); + private Types.SessionToken createSession(Service.CreateRequest request, CallContext ctx) { + var service = deadLineAfter(serviceBlockingStub, ctx.getTimeout(), ctx.getTimeUnit()); + var response = service.create(request); Verifier.checkResponse(response); diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/SessionToolsImpl.java b/client/src/main/java/info/frostfs/sdk/services/impl/SessionToolsImpl.java index 1ee5cbd..c51b673 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/SessionToolsImpl.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/SessionToolsImpl.java @@ -1,12 +1,14 @@ package info.frostfs.sdk.services.impl; -import frostfs.session.Types; import info.frostfs.sdk.dto.session.SessionToken; +import info.frostfs.sdk.exceptions.FrostFSException; import info.frostfs.sdk.jdo.ClientEnvironment; -import info.frostfs.sdk.mappers.session.SessionMapper; +import info.frostfs.sdk.jdo.parameters.CallContext; +import info.frostfs.sdk.jdo.parameters.session.PrmSessionCreate; import info.frostfs.sdk.services.ContextAccessor; import info.frostfs.sdk.services.SessionTools; +import static info.frostfs.sdk.constants.ErrorConst.SESSION_CREATE_FAILED; import static java.util.Objects.isNull; public class SessionToolsImpl extends ContextAccessor implements SessionTools { @@ -16,11 +18,18 @@ public class SessionToolsImpl extends ContextAccessor implements SessionTools { } @Override - public Types.SessionToken getOrCreateSession(SessionToken sessionToken, ClientEnvironment env) { - if (isNull(sessionToken)) { - return env.getFrostFSClient().createSessionInternal(-1); + public SessionToken getOrCreateSession(ClientEnvironment env, CallContext ctx) { + var token = env.getSessionCache().tryGetValue(env.getSessionKey()); + + if (isNull(token)) { + token = env.getFrostFSClient().createSession(new PrmSessionCreate(-1), ctx); + if (isNull(token)) { + throw new FrostFSException(SESSION_CREATE_FAILED); + } + + env.getSessionCache().setValue(env.getSessionKey(), token); } - return SessionMapper.deserializeSessionToken(sessionToken.getToken()); + return token; } } diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/Configuration.java b/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/Configuration.java index d4adcca..cfc9193 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/Configuration.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/interceptor/Configuration.java @@ -2,18 +2,21 @@ package info.frostfs.sdk.services.impl.interceptor; import io.prometheus.client.CollectorRegistry; + import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; + public class Configuration { private static final double[] DEFAULT_LATENCY_BUCKETS = - new double[] {.001, .005, .01, .05, 0.075, .1, .25, .5, 1, 2, 5, 10}; + new double[]{.001, .005, .01, .05, 0.075, .1, .25, .5, 1, 2, 5, 10}; private final boolean isIncludeLatencyHistograms; private final CollectorRegistry collectorRegistry; private final double[] latencyBuckets; private final List labelHeaders; private final boolean isAddCodeLabelToHistograms; + private Configuration( boolean isIncludeLatencyHistograms, CollectorRegistry collectorRegistry, @@ -28,7 +31,9 @@ public class Configuration { } - /** Returns a {@link Configuration} for recording all cheap metrics about the rpcs. */ + /** + * Returns a {@link Configuration} for recording all cheap metrics about the rpcs. + */ public static Configuration cheapMetricsOnly() { return new Configuration( false /* isIncludeLatencyHistograms */, @@ -119,27 +124,37 @@ public class Configuration { true /* isAddCodeLabelToHistograms */); } - /** Returns whether or not latency histograms for calls should be included. */ + /** + * Returns whether or not latency histograms for calls should be included. + */ public boolean isIncludeLatencyHistograms() { return isIncludeLatencyHistograms; } - /** Returns the {@link CollectorRegistry} used to record stats. */ + /** + * Returns the {@link CollectorRegistry} used to record stats. + */ public CollectorRegistry getCollectorRegistry() { return collectorRegistry; } - /** Returns the histogram buckets to use for latency metrics. */ + /** + * Returns the histogram buckets to use for latency metrics. + */ public double[] getLatencyBuckets() { return latencyBuckets; } - /** Returns the configured list of headers to be used as labels. */ + /** + * Returns the configured list of headers to be used as labels. + */ public List getLabelHeaders() { return labelHeaders; } - /** Returns whether or not status code label should be added to latency histogram. */ + /** + * Returns whether or not status code label should be added to latency histogram. + */ public boolean isAddCodeLabelToHistograms() { return isAddCodeLabelToHistograms; } diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectStreamer.java b/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectStreamer.java new file mode 100644 index 0000000..0556fb5 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectStreamer.java @@ -0,0 +1,62 @@ +package info.frostfs.sdk.services.impl.rwhelper; + +import frostfs.object.ObjectServiceGrpc; +import frostfs.object.Service; +import info.frostfs.sdk.exceptions.ProcessFrostFSException; +import info.frostfs.sdk.utils.WaitUtil; +import io.grpc.stub.StreamObserver; +import lombok.Getter; + +import static info.frostfs.sdk.constants.ErrorConst.PROTO_MESSAGE_IS_EMPTY_TEMPLATE; +import static java.util.Objects.isNull; + +public class ObjectStreamer { + private static final long POLL_INTERVAL = 10; + private final StreamObserver requestObserver; + private final PutResponseCallback responseObserver; + + public ObjectStreamer(ObjectServiceGrpc.ObjectServiceStub objectServiceStub) { + PutResponseCallback responseObserver = new PutResponseCallback(); + + this.responseObserver = responseObserver; + this.requestObserver = objectServiceStub.put(responseObserver); + } + + public void write(Service.PutRequest request) { + if (isNull(request)) { + throw new ProcessFrostFSException( + String.format(PROTO_MESSAGE_IS_EMPTY_TEMPLATE, Service.PutRequest.class.getName()) + ); + } + requestObserver.onNext(request); + } + + public Service.PutResponse complete() { + requestObserver.onCompleted(); + + while (isNull(responseObserver.getResponse())) { + WaitUtil.sleep(POLL_INTERVAL); + } + + return responseObserver.getResponse(); + } + + @Getter + private static class PutResponseCallback implements StreamObserver { + private Service.PutResponse response; + + @Override + public void onNext(Service.PutResponse putResponse) { + this.response = putResponse; + } + + @Override + public void onError(Throwable throwable) { + throw new ProcessFrostFSException(throwable); + } + + @Override + public void onCompleted() { + } + } +} diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectWriter.java b/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectWriter.java index f082507..76327f5 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectWriter.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectWriter.java @@ -1,63 +1,41 @@ package info.frostfs.sdk.services.impl.rwhelper; -import frostfs.object.ObjectServiceGrpc; +import com.google.protobuf.ByteString; import frostfs.object.Service; -import info.frostfs.sdk.exceptions.ProcessFrostFSException; -import info.frostfs.sdk.utils.WaitUtil; -import io.grpc.stub.StreamObserver; +import info.frostfs.sdk.dto.object.ObjectId; +import info.frostfs.sdk.jdo.ClientEnvironment; +import info.frostfs.sdk.jdo.parameters.object.PrmObjectPutBase; +import info.frostfs.sdk.tools.Verifier; +import lombok.AllArgsConstructor; import lombok.Getter; -import static info.frostfs.sdk.constants.ErrorConst.PROTO_MESSAGE_IS_EMPTY_TEMPLATE; -import static java.util.Objects.isNull; +import static info.frostfs.sdk.tools.RequestSigner.sign; +//todo specify a deadline for each stream request, not for the entire stream +@Getter +@AllArgsConstructor public class ObjectWriter { - private static final long POLL_INTERVAL = 10; - private final StreamObserver requestObserver; - private final PutResponseCallback responseObserver; + private final ClientEnvironment environment; + private final PrmObjectPutBase args; + private final ObjectStreamer streamer; - public ObjectWriter(ObjectServiceGrpc.ObjectServiceStub objectServiceStub) { - PutResponseCallback responseObserver = new PutResponseCallback(); + public void write(byte[] buffer) { + var body = Service.PutRequest.Body.newBuilder() + .setChunk(ByteString.copyFrom(buffer)) + .build(); + var chunkRequest = Service.PutRequest.newBuilder() + .setBody(body) + .clearVerifyHeader(); - this.responseObserver = responseObserver; - this.requestObserver = objectServiceStub.put(responseObserver); + sign(chunkRequest, environment.getKey()); + + streamer.write(chunkRequest.build()); } - public void write(Service.PutRequest request) { - if (isNull(request)) { - throw new ProcessFrostFSException( - String.format(PROTO_MESSAGE_IS_EMPTY_TEMPLATE, Service.PutRequest.class.getName()) - ); - } + public ObjectId complete() { + var response = streamer.complete(); + Verifier.checkResponse(response); - requestObserver.onNext(request); - } - - public Service.PutResponse complete() { - requestObserver.onCompleted(); - - while (isNull(responseObserver.getResponse())) { - WaitUtil.sleep(POLL_INTERVAL); - } - - return responseObserver.getResponse(); - } - - @Getter - private static class PutResponseCallback implements StreamObserver { - private Service.PutResponse response; - - @Override - public void onNext(Service.PutResponse putResponse) { - this.response = putResponse; - } - - @Override - public void onError(Throwable throwable) { - throw new ProcessFrostFSException(throwable); - } - - @Override - public void onCompleted() { - } + return new ObjectId(response.getBody().getObjectId().getValue().toByteArray()); } } diff --git a/client/src/main/java/info/frostfs/sdk/tools/RequestConstructor.java b/client/src/main/java/info/frostfs/sdk/tools/RequestConstructor.java index 9ad7457..af2150d 100644 --- a/client/src/main/java/info/frostfs/sdk/tools/RequestConstructor.java +++ b/client/src/main/java/info/frostfs/sdk/tools/RequestConstructor.java @@ -4,9 +4,11 @@ import com.google.protobuf.ByteString; import com.google.protobuf.Message; import frostfs.session.Types; import info.frostfs.sdk.dto.response.MetaHeader; +import info.frostfs.sdk.dto.session.SessionToken; import info.frostfs.sdk.exceptions.ValidationFrostFSException; import info.frostfs.sdk.jdo.ECDsa; import info.frostfs.sdk.mappers.response.MetaHeaderMapper; +import info.frostfs.sdk.mappers.session.SessionMapper; import org.apache.commons.collections4.MapUtils; import java.util.Map; @@ -65,13 +67,11 @@ public class RequestConstructor { setField(request, META_HEADER_FIELD_NAME, metaHeader.build()); } - public static Types.SessionToken createObjectTokenContext(Types.SessionToken sessionToken, + public static Types.SessionToken createObjectTokenContext(SessionToken sessionToken, frostfs.refs.Types.Address address, Types.ObjectSessionContext.Verb verb, ECDsa key) { - if (isNull(sessionToken) || sessionToken.getBody().getObject().getTarget().getSerializedSize() > 0) { - return sessionToken; - } + var protoToken = SessionMapper.deserializeSessionToken(sessionToken.getToken()); var target = Types.ObjectSessionContext.Target.newBuilder() .setContainer(address.getContainerId()); @@ -84,25 +84,23 @@ public class RequestConstructor { .setTarget(target.build()) .setVerb(verb) .build(); - var body = sessionToken.getBody().toBuilder() + var body = protoToken.getBody().toBuilder() .setObject(ctx) .setSessionKey(ByteString.copyFrom(key.getPublicKeyByte())) .build(); - return sessionToken.toBuilder() + return protoToken.toBuilder() .setSignature(signMessagePart(key, body)) .setBody(body) .build(); } - public static Types.SessionToken createContainerTokenContext(Types.SessionToken sessionToken, + public static Types.SessionToken createContainerTokenContext(SessionToken sessionToken, frostfs.refs.Types.ContainerID containerId, Types.ContainerSessionContext.Verb verb, frostfs.refs.Types.OwnerID ownerId, ECDsa key) { - if (isNull(sessionToken) || sessionToken.getBody().getContainer().getContainerId().getSerializedSize() > 0) { - return sessionToken; - } + var protoToken = SessionMapper.deserializeSessionToken(sessionToken.getToken()); var containerSession = Types.ContainerSessionContext.newBuilder().setVerb(verb); @@ -112,7 +110,7 @@ public class RequestConstructor { containerSession.setContainerId(containerId); } - var bodyBuilder = sessionToken.getBody().toBuilder() + var bodyBuilder = protoToken.getBody().toBuilder() .setContainer(containerSession) .setSessionKey(ByteString.copyFrom(key.getPublicKeyByte())); @@ -122,7 +120,7 @@ public class RequestConstructor { var body = bodyBuilder.build(); - return sessionToken.toBuilder() + return protoToken.toBuilder() .setSignature(signMessagePart(key, body)) .setBody(body) .build(); diff --git a/client/src/main/java/info/frostfs/sdk/utils/DeadLineUtil.java b/client/src/main/java/info/frostfs/sdk/utils/DeadLineUtil.java new file mode 100644 index 0000000..96ea46f --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/utils/DeadLineUtil.java @@ -0,0 +1,25 @@ +package info.frostfs.sdk.utils; + +import info.frostfs.sdk.exceptions.ValidationFrostFSException; +import io.grpc.stub.AbstractStub; + +import java.util.concurrent.TimeUnit; + +import static info.frostfs.sdk.constants.ErrorConst.PARAM_IS_MISSING_TEMPLATE; +import static java.util.Objects.isNull; + +public class DeadLineUtil { + private DeadLineUtil() { + } + + public static > T deadLineAfter(T stub, long deadLine, TimeUnit timeUnit) { + if (isNull(stub)) { + throw new ValidationFrostFSException( + String.format(PARAM_IS_MISSING_TEMPLATE, AbstractStub.class.getName()) + ); + } + + timeUnit = isNull(timeUnit) ? TimeUnit.MILLISECONDS : timeUnit; + return deadLine > 0 ? stub.withDeadlineAfter(deadLine, timeUnit) : stub; + } +} diff --git a/client/src/test/java/info/frostfs/sdk/services/AccountingClientTest.java b/client/src/test/java/info/frostfs/sdk/services/AccountingClientTest.java index e887223..b137af2 100644 --- a/client/src/test/java/info/frostfs/sdk/services/AccountingClientTest.java +++ b/client/src/test/java/info/frostfs/sdk/services/AccountingClientTest.java @@ -5,6 +5,7 @@ import frostfs.accounting.Service; import info.frostfs.sdk.Base58; import info.frostfs.sdk.dto.object.OwnerId; import info.frostfs.sdk.jdo.ClientEnvironment; +import info.frostfs.sdk.jdo.parameters.CallContext; import info.frostfs.sdk.services.impl.AccountingClientImpl; import info.frostfs.sdk.testgenerator.AccountingGenerator; import info.frostfs.sdk.tools.RequestConstructor; @@ -81,11 +82,11 @@ class AccountingClientTest { when(AccountingServiceClient.balance(captor.capture())).thenReturn(response); //When - var result = accountingClient.getBalance(); + var result = accountingClient.getBalance(new CallContext(0, null)); //Then requestConstructorMock.verify( - () -> RequestConstructor.addMetaHeader(any(Service.BalanceRequest.Builder.class), eq(null)), + () -> RequestConstructor.addMetaHeader(any(Service.BalanceRequest.Builder.class)), times(1) ); requestSignerMock.verify( diff --git a/client/src/test/java/info/frostfs/sdk/services/ApeManagerClientTest.java b/client/src/test/java/info/frostfs/sdk/services/ApeManagerClientTest.java index 7cdfcff..fa09315 100644 --- a/client/src/test/java/info/frostfs/sdk/services/ApeManagerClientTest.java +++ b/client/src/test/java/info/frostfs/sdk/services/ApeManagerClientTest.java @@ -8,6 +8,10 @@ import info.frostfs.sdk.dto.chain.ChainTarget; import info.frostfs.sdk.enums.TargetType; import info.frostfs.sdk.exceptions.ValidationFrostFSException; import info.frostfs.sdk.jdo.ClientEnvironment; +import info.frostfs.sdk.jdo.parameters.CallContext; +import info.frostfs.sdk.jdo.parameters.ape.PrmApeChainAdd; +import info.frostfs.sdk.jdo.parameters.ape.PrmApeChainList; +import info.frostfs.sdk.jdo.parameters.ape.PrmApeChainRemove; import info.frostfs.sdk.services.impl.ApeManagerClientImpl; import info.frostfs.sdk.testgenerator.ApeManagerGenerator; import info.frostfs.sdk.tools.RequestConstructor; @@ -78,6 +82,7 @@ class ApeManagerClientTest { //Given Chain chain = generateChain(); ChainTarget chainTarget = generateChainTarget(); + PrmApeChainAdd params = new PrmApeChainAdd(chain, chainTarget); var response = ApeManagerGenerator.generateAddChainResponse(); @@ -86,7 +91,7 @@ class ApeManagerClientTest { when(apeManagerServiceClient.addChain(captor.capture())).thenReturn(response); //When - var result = apeManagerClient.addChain(chain, chainTarget); + var result = apeManagerClient.addChain(params, new CallContext(0, null)); //Then requestConstructorMock.verify( @@ -112,11 +117,15 @@ class ApeManagerClientTest { //Given Chain chain = generateChain(); ChainTarget chainTarget = generateChainTarget(); + PrmApeChainAdd params1 = new PrmApeChainAdd(null, chainTarget); + PrmApeChainAdd params2 = new PrmApeChainAdd(chain, null); + PrmApeChainAdd params3 = new PrmApeChainAdd(null, null); + //When + Then - assertThrows(ValidationFrostFSException.class, () -> apeManagerClient.addChain(null, chainTarget)); - assertThrows(ValidationFrostFSException.class, () -> apeManagerClient.addChain(chain, null)); - assertThrows(ValidationFrostFSException.class, () -> apeManagerClient.addChain(null, null)); + assertThrows(ValidationFrostFSException.class, () -> apeManagerClient.addChain(params1, new CallContext())); + assertThrows(ValidationFrostFSException.class, () -> apeManagerClient.addChain(params2, new CallContext())); + assertThrows(ValidationFrostFSException.class, () -> apeManagerClient.addChain(params3, new CallContext())); } @Test @@ -124,6 +133,7 @@ class ApeManagerClientTest { //Given Chain chain = generateChain(); ChainTarget chainTarget = generateChainTarget(); + PrmApeChainRemove params = new PrmApeChainRemove(chain, chainTarget); var response = ApeManagerGenerator.generateRemoveChainResponse(); @@ -132,7 +142,7 @@ class ApeManagerClientTest { when(apeManagerServiceClient.removeChain(captor.capture())).thenReturn(response); //When - apeManagerClient.removeChain(chain, chainTarget); + apeManagerClient.removeChain(params, new CallContext(0, null)); //Then requestConstructorMock.verify( @@ -156,17 +166,21 @@ class ApeManagerClientTest { //Given Chain chain = generateChain(); ChainTarget chainTarget = generateChainTarget(); + PrmApeChainRemove params1 = new PrmApeChainRemove(null, chainTarget); + PrmApeChainRemove params2 = new PrmApeChainRemove(chain, null); + PrmApeChainRemove params3 = new PrmApeChainRemove(null, null); //When + Then - assertThrows(ValidationFrostFSException.class, () -> apeManagerClient.removeChain(null, chainTarget)); - assertThrows(ValidationFrostFSException.class, () -> apeManagerClient.removeChain(chain, null)); - assertThrows(ValidationFrostFSException.class, () -> apeManagerClient.removeChain(null, null)); + assertThrows(ValidationFrostFSException.class, () -> apeManagerClient.removeChain(params1, new CallContext())); + assertThrows(ValidationFrostFSException.class, () -> apeManagerClient.removeChain(params2, new CallContext())); + assertThrows(ValidationFrostFSException.class, () -> apeManagerClient.removeChain(params3, new CallContext())); } @Test void listChain_success() { //Given ChainTarget chainTarget = generateChainTarget(); + PrmApeChainList params = new PrmApeChainList(chainTarget); var response = ApeManagerGenerator.generateListChainsResponse(); @@ -175,7 +189,7 @@ class ApeManagerClientTest { when(apeManagerServiceClient.listChains(captor.capture())).thenReturn(response); //When - var result = apeManagerClient.listChains(chainTarget); + var result = apeManagerClient.listChains(params, new CallContext(0, null)); //Then requestConstructorMock.verify( @@ -202,7 +216,8 @@ class ApeManagerClientTest { @Test void listChain_wrongParams() { //When + Then - assertThrows(ValidationFrostFSException.class, () -> apeManagerClient.listChains(null)); + assertThrows(ValidationFrostFSException.class, + () -> apeManagerClient.listChains(new PrmApeChainList(null), new CallContext())); } private Chain generateChain() { diff --git a/cryptography/pom.xml b/cryptography/pom.xml index 606a193..ac56a86 100644 --- a/cryptography/pom.xml +++ b/cryptography/pom.xml @@ -6,7 +6,7 @@ info.frostfs.sdk frostfs-sdk-java - 0.2.0 + 0.3.0 cryptography @@ -21,7 +21,7 @@ info.frostfs.sdk exceptions - 0.2.0 + 0.3.0 com.google.protobuf diff --git a/exceptions/pom.xml b/exceptions/pom.xml index a24f1fc..4cb8bcc 100644 --- a/exceptions/pom.xml +++ b/exceptions/pom.xml @@ -6,7 +6,7 @@ info.frostfs.sdk frostfs-sdk-java - 0.2.0 + 0.3.0 exceptions diff --git a/exceptions/src/main/java/info/frostfs/sdk/constants/ErrorConst.java b/exceptions/src/main/java/info/frostfs/sdk/constants/ErrorConst.java index 26f3eac..62c3eb8 100644 --- a/exceptions/src/main/java/info/frostfs/sdk/constants/ErrorConst.java +++ b/exceptions/src/main/java/info/frostfs/sdk/constants/ErrorConst.java @@ -46,6 +46,8 @@ public class ErrorConst { public static final String POOL_CLIENTS_UNHEALTHY = "cannot find alive client"; public static final String POOL_NOT_DIALED = "pool not dialed"; + public static final String SESSION_CREATE_FAILED = "cannot create session"; + public static final String FIELDS_DELIMITER_COMMA = ", "; public static final String FIELDS_DELIMITER_OR = " or "; diff --git a/models/pom.xml b/models/pom.xml index 08dbdc6..32cade4 100644 --- a/models/pom.xml +++ b/models/pom.xml @@ -6,7 +6,7 @@ info.frostfs.sdk frostfs-sdk-java - 0.2.0 + 0.3.0 models @@ -21,17 +21,17 @@ info.frostfs.sdk cryptography - 0.2.0 + 0.3.0 info.frostfs.sdk protos - 0.2.0 + 0.3.0 info.frostfs.sdk exceptions - 0.2.0 + 0.3.0 diff --git a/models/src/main/java/info/frostfs/sdk/constants/AppConst.java b/models/src/main/java/info/frostfs/sdk/constants/AppConst.java index 67a2422..35d64c6 100644 --- a/models/src/main/java/info/frostfs/sdk/constants/AppConst.java +++ b/models/src/main/java/info/frostfs/sdk/constants/AppConst.java @@ -12,6 +12,7 @@ public class AppConst { public static final int OBJECT_CHUNK_SIZE = 3 * MIB; public static final int SHA256_HASH_LENGTH = 32; public static final int UUID_BYTE_ARRAY_LENGTH = 16; + public static final int DEFAULT_GRPC_TIMEOUT = 5; private AppConst() { } diff --git a/models/src/main/java/info/frostfs/sdk/dto/object/SplitInfo.java b/models/src/main/java/info/frostfs/sdk/dto/object/SplitInfo.java new file mode 100644 index 0000000..cbd9e17 --- /dev/null +++ b/models/src/main/java/info/frostfs/sdk/dto/object/SplitInfo.java @@ -0,0 +1,17 @@ +package info.frostfs.sdk.dto.object; + +import frostfs.object.Types; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class SplitInfo { + private final Types.SplitInfo splitInfo; + + private final SplitId splitId; + + private final ObjectId link; + + private final ObjectId lastPart; +} diff --git a/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerMapper.java index 9b9dd72..f0a8196 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerMapper.java @@ -13,7 +13,6 @@ import java.util.stream.Collectors; import static info.frostfs.sdk.UuidExtension.asBytes; import static info.frostfs.sdk.UuidExtension.asUuid; -import static info.frostfs.sdk.constants.AttributeConst.DISABLE_HOMOMORPHIC_HASHING_ATTRIBUTE; import static info.frostfs.sdk.constants.ErrorConst.UNKNOWN_ENUM_VALUE_TEMPLATE; import static java.util.Objects.isNull; @@ -31,7 +30,6 @@ public class ContainerMapper { .setPlacementPolicy(PlacementPolicyMapper.toGrpcMessage(container.getPlacementPolicy())) .setNonce(ByteString.copyFrom(asBytes(container.getNonce()))); - container.getAttributes().putIfAbsent(DISABLE_HOMOMORPHIC_HASHING_ATTRIBUTE, Boolean.TRUE.toString()); var attributes = container.getAttributes().entrySet().stream() .map(entry -> Types.Container.Attribute.newBuilder() diff --git a/models/src/main/java/info/frostfs/sdk/mappers/object/SplitInfoMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/object/SplitInfoMapper.java new file mode 100644 index 0000000..ac5f6ae --- /dev/null +++ b/models/src/main/java/info/frostfs/sdk/mappers/object/SplitInfoMapper.java @@ -0,0 +1,30 @@ +package info.frostfs.sdk.mappers.object; + +import frostfs.object.Types; +import info.frostfs.sdk.dto.object.ObjectId; +import info.frostfs.sdk.dto.object.SplitId; +import info.frostfs.sdk.dto.object.SplitInfo; + +import static info.frostfs.sdk.UuidExtension.asUuid; +import static java.util.Objects.isNull; + +public class SplitInfoMapper { + private SplitInfoMapper() { + } + + public static SplitInfo toModel(Types.SplitInfo splitInfo) { + if (isNull(splitInfo) || splitInfo.getSerializedSize() == 0) { + return null; + } + + var splitId = new SplitId(asUuid(splitInfo.getSplitId().toByteArray())); + var link = splitInfo.getLink().getSerializedSize() == 0 + ? null + : new ObjectId(splitInfo.getLink().getValue().toByteArray()); + var lastPart = splitInfo.getLastPart().getSerializedSize() == 0 + ? null + : new ObjectId(splitInfo.getLastPart().getValue().toByteArray()); + + return new SplitInfo(splitInfo, splitId, link, lastPart); + } +} diff --git a/models/src/test/java/info/frostfs/sdk/mappers/object/SplitInfoMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/object/SplitInfoMapperTest.java new file mode 100644 index 0000000..3e9ca2b --- /dev/null +++ b/models/src/test/java/info/frostfs/sdk/mappers/object/SplitInfoMapperTest.java @@ -0,0 +1,61 @@ +package info.frostfs.sdk.mappers.object; + +import com.google.protobuf.ByteString; +import frostfs.object.Types; +import info.frostfs.sdk.dto.object.ObjectId; +import info.frostfs.sdk.dto.object.SplitId; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; + +public class SplitInfoMapperTest { + @Test + void toModel_successLastPart() { + //Given + var splitId = new SplitId(); + var objectId = new ObjectId("85orCLKSu3X1jGiTFmwmTUsBU88RBARNwuRwrEy5pyww"); + var splitInfo = Types.SplitInfo.newBuilder() + .setSplitId(ByteString.copyFrom(splitId.toBinary())) + .setLastPart(ObjectIdMapper.toGrpcMessage(objectId)) + .build(); + + //When + var result = SplitInfoMapper.toModel(splitInfo); + + //Then + assertNotNull(result); + assertNull(result.getLink()); + assertThat(result.getSplitInfo()).isEqualTo(splitInfo); + assertThat(result.getSplitId().toBinary()).containsExactly(splitId.toBinary()); + assertEquals(objectId.getValue(), result.getLastPart().getValue()); + } + + @Test + void toModel_successLink() { + //Given + var splitId = new SplitId(); + var objectId = new ObjectId("85orCLKSu3X1jGiTFmwmTUsBU88RBARNwuRwrEy5pyww"); + var splitInfo = Types.SplitInfo.newBuilder() + .setSplitId(ByteString.copyFrom(splitId.toBinary())) + .setLink(ObjectIdMapper.toGrpcMessage(objectId)) + .build(); + + //When + var result = SplitInfoMapper.toModel(splitInfo); + + //Then + assertNotNull(result); + assertNull(result.getLastPart()); + assertThat(result.getSplitInfo()).isEqualTo(splitInfo); + assertThat(result.getSplitId().toBinary()).containsExactly(splitId.toBinary()); + assertEquals(objectId.getValue(), result.getLink().getValue()); + } + + @Test + void toModel_null() { + //When + Then + assertNull(SplitInfoMapper.toModel(null)); + assertNull(SplitInfoMapper.toModel(Types.SplitInfo.getDefaultInstance())); + } +} diff --git a/pom.xml b/pom.xml index 2c9f30e..72eb9eb 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ info.frostfs.sdk frostfs-sdk-java - 0.2.0 + 0.3.0 pom client diff --git a/protos/pom.xml b/protos/pom.xml index d08ae98..d3a2b3b 100644 --- a/protos/pom.xml +++ b/protos/pom.xml @@ -6,7 +6,7 @@ info.frostfs.sdk frostfs-sdk-java - 0.2.0 + 0.3.0 protos From 33f7198ace60cab37447c225e6f85f48127ca9ed Mon Sep 17 00:00:00 2001 From: Ori Bruk Date: Wed, 29 Jan 2025 14:44:59 +0300 Subject: [PATCH 29/38] [#37] Add tree service methods: getRange(), getRangeHash(), patchObject(). Maven project version inheritance. Signed-off-by: Ori Bruk --- client/pom.xml | 8 +- .../java/info/frostfs/sdk/FrostFSClient.java | 20 +++ .../object/patch/PrmObjectPatch.java | 42 +++++ .../parameters/object/patch/PrmRangeGet.java | 35 +++++ .../object/patch/PrmRangeHashGet.java | 38 +++++ .../main/java/info/frostfs/sdk/pool/Pool.java | 23 +++ .../frostfs/sdk/services/CommonClient.java | 4 + .../frostfs/sdk/services/ObjectClient.java | 10 ++ .../sdk/services/impl/ObjectClientImpl.java | 148 +++++++++++++++++- .../impl/rwhelper/ObjectStreamer.java | 4 +- .../services/impl/rwhelper/PatchStreamer.java | 62 ++++++++ .../services/impl/rwhelper/RangeReader.java | 25 +++ cryptography/pom.xml | 4 +- exceptions/pom.xml | 2 +- models/pom.xml | 8 +- .../info/frostfs/sdk/constants/AppConst.java | 1 + .../frostfs/sdk/dto/object/patch/Address.java | 13 ++ .../frostfs/sdk/dto/object/patch/Range.java | 11 ++ .../mappers/object/ObjectAttributeMapper.java | 12 ++ .../mappers/object/patch/AddressMapper.java | 25 +++ .../sdk/mappers/object/patch/RangeMapper.java | 35 +++++ .../object/ObjectAttributeMapperTest.java | 29 ++++ .../object/patch/AddressMapperTest.java | 39 +++++ .../mappers/object/patch/RangeMapperTest.java | 58 +++++++ pom.xml | 4 +- protos/pom.xml | 2 +- 26 files changed, 642 insertions(+), 20 deletions(-) create mode 100644 client/src/main/java/info/frostfs/sdk/jdo/parameters/object/patch/PrmObjectPatch.java create mode 100644 client/src/main/java/info/frostfs/sdk/jdo/parameters/object/patch/PrmRangeGet.java create mode 100644 client/src/main/java/info/frostfs/sdk/jdo/parameters/object/patch/PrmRangeHashGet.java create mode 100644 client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/PatchStreamer.java create mode 100644 client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/RangeReader.java create mode 100644 models/src/main/java/info/frostfs/sdk/dto/object/patch/Address.java create mode 100644 models/src/main/java/info/frostfs/sdk/dto/object/patch/Range.java create mode 100644 models/src/main/java/info/frostfs/sdk/mappers/object/patch/AddressMapper.java create mode 100644 models/src/main/java/info/frostfs/sdk/mappers/object/patch/RangeMapper.java create mode 100644 models/src/test/java/info/frostfs/sdk/mappers/object/patch/AddressMapperTest.java create mode 100644 models/src/test/java/info/frostfs/sdk/mappers/object/patch/RangeMapperTest.java diff --git a/client/pom.xml b/client/pom.xml index f907964..2d87b21 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -6,7 +6,7 @@ info.frostfs.sdk frostfs-sdk-java - 0.3.0 + ${revision} client @@ -21,17 +21,17 @@ info.frostfs.sdk cryptography - 0.3.0 + ${revision} info.frostfs.sdk models - 0.3.0 + ${revision} info.frostfs.sdk exceptions - 0.3.0 + ${revision} commons-codec diff --git a/client/src/main/java/info/frostfs/sdk/FrostFSClient.java b/client/src/main/java/info/frostfs/sdk/FrostFSClient.java index 31a76b7..e6704ed 100644 --- a/client/src/main/java/info/frostfs/sdk/FrostFSClient.java +++ b/client/src/main/java/info/frostfs/sdk/FrostFSClient.java @@ -24,6 +24,9 @@ import info.frostfs.sdk.jdo.parameters.container.PrmContainerDelete; import info.frostfs.sdk.jdo.parameters.container.PrmContainerGet; import info.frostfs.sdk.jdo.parameters.container.PrmContainerGetAll; import info.frostfs.sdk.jdo.parameters.object.*; +import info.frostfs.sdk.jdo.parameters.object.patch.PrmObjectPatch; +import info.frostfs.sdk.jdo.parameters.object.patch.PrmRangeGet; +import info.frostfs.sdk.jdo.parameters.object.patch.PrmRangeHashGet; import info.frostfs.sdk.jdo.parameters.session.PrmSessionCreate; import info.frostfs.sdk.jdo.result.ObjectHeaderResult; import info.frostfs.sdk.pool.SessionCache; @@ -33,6 +36,7 @@ import info.frostfs.sdk.services.impl.*; import info.frostfs.sdk.services.impl.interceptor.Configuration; import info.frostfs.sdk.services.impl.interceptor.MonitoringClientInterceptor; import info.frostfs.sdk.services.impl.rwhelper.ObjectWriter; +import info.frostfs.sdk.services.impl.rwhelper.RangeReader; import info.frostfs.sdk.utils.Validator; import io.grpc.Channel; import io.grpc.ClientInterceptors; @@ -164,6 +168,21 @@ public class FrostFSClient implements CommonClient { return objectClientImpl.searchObjects(args, ctx); } + @Override + public RangeReader getRange(PrmRangeGet args, CallContext ctx) { + return objectClientImpl.getRange(args, ctx); + } + + @Override + public byte[][] getRangeHash(PrmRangeHashGet args, CallContext ctx) { + return objectClientImpl.getRangeHash(args, ctx); + } + + @Override + public ObjectId patchObject(PrmObjectPatch args, CallContext ctx) { + return objectClientImpl.patchObject(args, ctx); + } + @Override public byte[] addChain(PrmApeChainAdd args, CallContext ctx) { return apeManagerClient.addChain(args, ctx); @@ -213,6 +232,7 @@ public class FrostFSClient implements CommonClient { return accountingClient.getBalance(ctx); } + @Override public String dial(CallContext ctx) { accountingClient.getBalance(ctx); return null; diff --git a/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/patch/PrmObjectPatch.java b/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/patch/PrmObjectPatch.java new file mode 100644 index 0000000..9f93c9a --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/patch/PrmObjectPatch.java @@ -0,0 +1,42 @@ +package info.frostfs.sdk.jdo.parameters.object.patch; + +import info.frostfs.sdk.annotations.NotNull; +import info.frostfs.sdk.dto.object.ObjectAttribute; +import info.frostfs.sdk.dto.object.patch.Address; +import info.frostfs.sdk.dto.object.patch.Range; +import info.frostfs.sdk.dto.session.SessionToken; +import info.frostfs.sdk.jdo.parameters.session.SessionContext; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +import java.io.InputStream; +import java.util.List; +import java.util.Map; + +@Getter +@Builder +@AllArgsConstructor +public class PrmObjectPatch implements SessionContext { + @NotNull + private Address address; + + @NotNull + private Range range; + + @NotNull + private InputStream payload; + + private List newAttributes; + private boolean replaceAttributes; + private int maxChunkLength; + private SessionToken sessionToken; + private Map xHeaders; + + public PrmObjectPatch(Address address, Range range, InputStream payload, int maxChunkLength) { + this.address = address; + this.range = range; + this.payload = payload; + this.maxChunkLength = maxChunkLength; + } +} diff --git a/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/patch/PrmRangeGet.java b/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/patch/PrmRangeGet.java new file mode 100644 index 0000000..add15b7 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/patch/PrmRangeGet.java @@ -0,0 +1,35 @@ +package info.frostfs.sdk.jdo.parameters.object.patch; + +import info.frostfs.sdk.annotations.NotNull; +import info.frostfs.sdk.dto.container.ContainerId; +import info.frostfs.sdk.dto.object.ObjectId; +import info.frostfs.sdk.dto.object.patch.Range; +import info.frostfs.sdk.dto.session.SessionToken; +import info.frostfs.sdk.jdo.parameters.session.SessionContext; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +import java.util.Map; + +@Getter +@Builder +@AllArgsConstructor +public class PrmRangeGet implements SessionContext { + @NotNull + private ContainerId containerId; + @NotNull + private ObjectId objectId; + @NotNull + private Range range; + + private boolean raw; + private SessionToken sessionToken; + private Map xHeaders; + + public PrmRangeGet(ContainerId containerId, ObjectId objectId, Range range) { + this.containerId = containerId; + this.objectId = objectId; + this.range = range; + } +} diff --git a/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/patch/PrmRangeHashGet.java b/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/patch/PrmRangeHashGet.java new file mode 100644 index 0000000..d860133 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/patch/PrmRangeHashGet.java @@ -0,0 +1,38 @@ +package info.frostfs.sdk.jdo.parameters.object.patch; + +import info.frostfs.sdk.annotations.NotNull; +import info.frostfs.sdk.dto.container.ContainerId; +import info.frostfs.sdk.dto.object.ObjectId; +import info.frostfs.sdk.dto.object.patch.Range; +import info.frostfs.sdk.dto.session.SessionToken; +import info.frostfs.sdk.jdo.parameters.session.SessionContext; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +import java.util.List; +import java.util.Map; + +@Getter +@Builder +@AllArgsConstructor +public class PrmRangeHashGet implements SessionContext { + @NotNull + private ContainerId containerId; + @NotNull + private ObjectId objectId; + @NotNull + private List ranges; + @NotNull + private byte[] salt; + + private SessionToken sessionToken; + private Map xHeaders; + + public PrmRangeHashGet(ContainerId containerId, ObjectId objectId, List ranges, byte[] salt) { + this.containerId = containerId; + this.objectId = objectId; + this.ranges = ranges; + this.salt = salt; + } +} diff --git a/client/src/main/java/info/frostfs/sdk/pool/Pool.java b/client/src/main/java/info/frostfs/sdk/pool/Pool.java index 74143a2..45b818e 100644 --- a/client/src/main/java/info/frostfs/sdk/pool/Pool.java +++ b/client/src/main/java/info/frostfs/sdk/pool/Pool.java @@ -26,12 +26,16 @@ import info.frostfs.sdk.jdo.parameters.container.PrmContainerDelete; import info.frostfs.sdk.jdo.parameters.container.PrmContainerGet; import info.frostfs.sdk.jdo.parameters.container.PrmContainerGetAll; import info.frostfs.sdk.jdo.parameters.object.*; +import info.frostfs.sdk.jdo.parameters.object.patch.PrmObjectPatch; +import info.frostfs.sdk.jdo.parameters.object.patch.PrmRangeGet; +import info.frostfs.sdk.jdo.parameters.object.patch.PrmRangeHashGet; import info.frostfs.sdk.jdo.parameters.session.PrmSessionCreate; import info.frostfs.sdk.jdo.pool.NodeParameters; import info.frostfs.sdk.jdo.pool.PoolInitParameters; import info.frostfs.sdk.jdo.result.ObjectHeaderResult; import info.frostfs.sdk.services.CommonClient; import info.frostfs.sdk.services.impl.rwhelper.ObjectWriter; +import info.frostfs.sdk.services.impl.rwhelper.RangeReader; import info.frostfs.sdk.utils.FrostFSMessages; import info.frostfs.sdk.utils.WaitUtil; import lombok.Getter; @@ -198,6 +202,7 @@ public class Pool implements CommonClient { return address + key; } + @Override public String dial(CallContext ctx) { InnerPool[] inner = new InnerPool[rebalanceParams.getNodesParams().length]; boolean atLeastOneHealthy = false; @@ -479,6 +484,24 @@ public class Pool implements CommonClient { return client.getClient().searchObjects(args, ctx); } + @Override + public RangeReader getRange(PrmRangeGet args, CallContext ctx) { + ClientWrapper client = connection(); + return client.getClient().getRange(args, ctx); + } + + @Override + public byte[][] getRangeHash(PrmRangeHashGet args, CallContext ctx) { + ClientWrapper client = connection(); + return client.getClient().getRangeHash(args, ctx); + } + + @Override + public ObjectId patchObject(PrmObjectPatch args, CallContext ctx) { + ClientWrapper client = connection(); + return client.getClient().patchObject(args, ctx); + } + @Override public byte[] addChain(PrmApeChainAdd args, CallContext ctx) { ClientWrapper client = connection(); diff --git a/client/src/main/java/info/frostfs/sdk/services/CommonClient.java b/client/src/main/java/info/frostfs/sdk/services/CommonClient.java index e0482fb..ce0dcba 100644 --- a/client/src/main/java/info/frostfs/sdk/services/CommonClient.java +++ b/client/src/main/java/info/frostfs/sdk/services/CommonClient.java @@ -1,5 +1,9 @@ package info.frostfs.sdk.services; +import info.frostfs.sdk.jdo.parameters.CallContext; + public interface CommonClient extends AccountingClient, ApeManagerClient, ContainerClient, NetmapClient, ObjectClient, SessionClient, ToolsClient { + + String dial(CallContext ctx); } diff --git a/client/src/main/java/info/frostfs/sdk/services/ObjectClient.java b/client/src/main/java/info/frostfs/sdk/services/ObjectClient.java index 86901b5..5e8362c 100644 --- a/client/src/main/java/info/frostfs/sdk/services/ObjectClient.java +++ b/client/src/main/java/info/frostfs/sdk/services/ObjectClient.java @@ -4,8 +4,12 @@ import info.frostfs.sdk.dto.object.ObjectFrostFS; import info.frostfs.sdk.dto.object.ObjectId; import info.frostfs.sdk.jdo.parameters.CallContext; import info.frostfs.sdk.jdo.parameters.object.*; +import info.frostfs.sdk.jdo.parameters.object.patch.PrmObjectPatch; +import info.frostfs.sdk.jdo.parameters.object.patch.PrmRangeGet; +import info.frostfs.sdk.jdo.parameters.object.patch.PrmRangeHashGet; import info.frostfs.sdk.jdo.result.ObjectHeaderResult; import info.frostfs.sdk.services.impl.rwhelper.ObjectWriter; +import info.frostfs.sdk.services.impl.rwhelper.RangeReader; public interface ObjectClient { ObjectHeaderResult getObjectHead(PrmObjectHeadGet args, CallContext ctx); @@ -21,4 +25,10 @@ public interface ObjectClient { void deleteObject(PrmObjectDelete args, CallContext ctx); Iterable searchObjects(PrmObjectSearch args, CallContext ctx); + + RangeReader getRange(PrmRangeGet args, CallContext ctx); + + byte[][] getRangeHash(PrmRangeHashGet args, CallContext ctx); + + ObjectId patchObject(PrmObjectPatch args, CallContext ctx); } diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/ObjectClientImpl.java b/client/src/main/java/info/frostfs/sdk/services/impl/ObjectClientImpl.java index 203d1e6..266b630 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/ObjectClientImpl.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/ObjectClientImpl.java @@ -14,16 +14,18 @@ import info.frostfs.sdk.jdo.ClientEnvironment; import info.frostfs.sdk.jdo.PutObjectResult; import info.frostfs.sdk.jdo.parameters.CallContext; import info.frostfs.sdk.jdo.parameters.object.*; +import info.frostfs.sdk.jdo.parameters.object.patch.PrmObjectPatch; +import info.frostfs.sdk.jdo.parameters.object.patch.PrmRangeGet; +import info.frostfs.sdk.jdo.parameters.object.patch.PrmRangeHashGet; import info.frostfs.sdk.jdo.parameters.session.SessionContext; import info.frostfs.sdk.jdo.result.ObjectHeaderResult; import info.frostfs.sdk.mappers.container.ContainerIdMapper; import info.frostfs.sdk.mappers.object.*; +import info.frostfs.sdk.mappers.object.patch.AddressMapper; +import info.frostfs.sdk.mappers.object.patch.RangeMapper; import info.frostfs.sdk.services.ContextAccessor; import info.frostfs.sdk.services.ObjectClient; -import info.frostfs.sdk.services.impl.rwhelper.ObjectReaderImpl; -import info.frostfs.sdk.services.impl.rwhelper.ObjectStreamer; -import info.frostfs.sdk.services.impl.rwhelper.ObjectWriter; -import info.frostfs.sdk.services.impl.rwhelper.SearchReader; +import info.frostfs.sdk.services.impl.rwhelper.*; import info.frostfs.sdk.tools.RequestConstructor; import info.frostfs.sdk.tools.Verifier; import org.apache.commons.collections4.CollectionUtils; @@ -200,6 +202,77 @@ public class ObjectClientImpl extends ContextAccessor implements ObjectClient { return new ObjectId(grpcObject.getObjectId().getValue().toByteArray()); } + @Override + public RangeReader getRange(PrmRangeGet args, CallContext ctx) { + validate(args); + + var request = createGetRangeRequest(args, ctx); + + var service = deadLineAfter(objectServiceBlockingClient, ctx.getTimeout(), ctx.getTimeUnit()); + return new RangeReader(service.getRange(request)); + } + + @Override + public byte[][] getRangeHash(PrmRangeHashGet args, CallContext ctx) { + validate(args); + + var request = createGetRangeHashRequest(args, ctx); + + var service = deadLineAfter(objectServiceBlockingClient, ctx.getTimeout(), ctx.getTimeUnit()); + var response = service.getRangeHash(request); + + Verifier.checkResponse(response); + + return response.getBody().getHashListList().stream().map(ByteString::toByteArray).toArray(byte[][]::new); + } + + @Override + public ObjectId patchObject(PrmObjectPatch args, CallContext ctx) { + validate(args); + + var request = createInitPatchRequest(args); + var protoToken = RequestConstructor.createObjectTokenContext( + getOrCreateSession(args, ctx), + request.getBody().getAddress(), + frostfs.session.Types.ObjectSessionContext.Verb.PATCH, + getContext().getKey() + ); + + var currentPos = args.getRange().getOffset(); + var chunkSize = args.getMaxChunkLength(); + byte[] chunkBuffer = new byte[chunkSize]; + + var service = deadLineAfter(objectServiceClient, ctx.getTimeout(), ctx.getTimeUnit()); + PatchStreamer writer = new PatchStreamer(service); + + var bytesCount = readNBytes(args.getPayload(), chunkBuffer, chunkSize); + while (bytesCount > 0) { + var range = Service.Range.newBuilder() + .setOffset(currentPos) + .setLength(bytesCount) + .build(); + + Service.PatchRequest.Body.Patch.newBuilder() + .setChunk(ByteString.copyFrom(chunkBuffer, 0, bytesCount)) + .setSourceRange(range) + .build(); + + currentPos += bytesCount; + + RequestConstructor.addMetaHeader(request, args.getXHeaders(), protoToken); + sign(request, getContext().getKey()); + + writer.write(request.build()); + bytesCount = readNBytes(args.getPayload(), chunkBuffer, chunkSize); + } + + var response = writer.complete(); + + Verifier.checkResponse(response); + + return ObjectIdMapper.toModel(response.getBody().getObjectId()); + } + private ObjectFrostFS getObject(Service.GetRequest request, CallContext ctx) { var reader = getObjectInit(request, ctx); @@ -371,7 +444,6 @@ public class ObjectClientImpl extends ContextAccessor implements ObjectClient { } private Service.GetRequest createGetRequest(PrmObjectGet args, CallContext ctx) { - var address = Types.Address.newBuilder() .setContainerId(ContainerIdMapper.toGrpcMessage(args.getContainerId())) .setObjectId(ObjectIdMapper.toGrpcMessage(args.getObjectId())) @@ -510,4 +582,70 @@ public class ObjectClientImpl extends ContextAccessor implements ObjectClient { return request.build(); } + + private Service.GetRangeRequest createGetRangeRequest(PrmRangeGet args, CallContext ctx) { + var address = Types.Address.newBuilder() + .setContainerId(ContainerIdMapper.toGrpcMessage(args.getContainerId())) + .setObjectId(ObjectIdMapper.toGrpcMessage(args.getObjectId())) + .build(); + var body = Service.GetRangeRequest.Body.newBuilder() + .setAddress(address) + .setRange(RangeMapper.toGrpcMessage(args.getRange())) + .setRaw(args.isRaw()) + .build(); + var request = Service.GetRangeRequest.newBuilder() + .setBody(body); + + var sessionToken = getOrCreateSession(args, ctx); + var protoToken = RequestConstructor.createObjectTokenContext( + sessionToken, + address, + frostfs.session.Types.ObjectSessionContext.Verb.RANGE, + getContext().getKey() + ); + + RequestConstructor.addMetaHeader(request, args.getXHeaders(), protoToken); + sign(request, getContext().getKey()); + + return request.build(); + } + + private Service.GetRangeHashRequest createGetRangeHashRequest(PrmRangeHashGet args, CallContext ctx) { + var address = Types.Address.newBuilder() + .setContainerId(ContainerIdMapper.toGrpcMessage(args.getContainerId())) + .setObjectId(ObjectIdMapper.toGrpcMessage(args.getObjectId())) + .build(); + var body = Service.GetRangeHashRequest.Body.newBuilder() + .setAddress(address) + .setType(Types.ChecksumType.SHA256) + .setSalt(ByteString.copyFrom(args.getSalt())) + .addAllRanges(RangeMapper.toGrpcMessages(args.getRanges())) + .build(); + var request = Service.GetRangeHashRequest.newBuilder() + .setBody(body); + + var sessionToken = getOrCreateSession(args, ctx); + var protoToken = RequestConstructor.createObjectTokenContext( + sessionToken, + address, + frostfs.session.Types.ObjectSessionContext.Verb.RANGEHASH, + getContext().getKey() + ); + + RequestConstructor.addMetaHeader(request, args.getXHeaders(), protoToken); + sign(request, getContext().getKey()); + + return request.build(); + } + + private Service.PatchRequest.Builder createInitPatchRequest(PrmObjectPatch args) { + var address = AddressMapper.toGrpcMessage(args.getAddress()); + var body = Service.PatchRequest.Body.newBuilder() + .setAddress(address) + .setReplaceAttributes(args.isReplaceAttributes()) + .addAllNewAttributes(ObjectAttributeMapper.toGrpcMessages(args.getNewAttributes())) + .build(); + return Service.PatchRequest.newBuilder() + .setBody(body); + } } diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectStreamer.java b/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectStreamer.java index 0556fb5..cadfbf1 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectStreamer.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectStreamer.java @@ -7,11 +7,11 @@ import info.frostfs.sdk.utils.WaitUtil; import io.grpc.stub.StreamObserver; import lombok.Getter; +import static info.frostfs.sdk.constants.AppConst.DEFAULT_POLL_INTERVAL; import static info.frostfs.sdk.constants.ErrorConst.PROTO_MESSAGE_IS_EMPTY_TEMPLATE; import static java.util.Objects.isNull; public class ObjectStreamer { - private static final long POLL_INTERVAL = 10; private final StreamObserver requestObserver; private final PutResponseCallback responseObserver; @@ -35,7 +35,7 @@ public class ObjectStreamer { requestObserver.onCompleted(); while (isNull(responseObserver.getResponse())) { - WaitUtil.sleep(POLL_INTERVAL); + WaitUtil.sleep(DEFAULT_POLL_INTERVAL); } return responseObserver.getResponse(); diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/PatchStreamer.java b/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/PatchStreamer.java new file mode 100644 index 0000000..4cdee35 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/PatchStreamer.java @@ -0,0 +1,62 @@ +package info.frostfs.sdk.services.impl.rwhelper; + +import frostfs.object.ObjectServiceGrpc; +import frostfs.object.Service; +import info.frostfs.sdk.exceptions.ProcessFrostFSException; +import info.frostfs.sdk.utils.WaitUtil; +import io.grpc.stub.StreamObserver; +import lombok.Getter; + +import static info.frostfs.sdk.constants.AppConst.DEFAULT_POLL_INTERVAL; +import static info.frostfs.sdk.constants.ErrorConst.PROTO_MESSAGE_IS_EMPTY_TEMPLATE; +import static java.util.Objects.isNull; + +public class PatchStreamer { + private final StreamObserver requestObserver; + private final PatchResponseCallback responseObserver; + + public PatchStreamer(ObjectServiceGrpc.ObjectServiceStub objectServiceStub) { + PatchResponseCallback responseObserver = new PatchResponseCallback(); + + this.responseObserver = responseObserver; + this.requestObserver = objectServiceStub.patch(responseObserver); + } + + public void write(Service.PatchRequest request) { + if (isNull(request)) { + throw new ProcessFrostFSException( + String.format(PROTO_MESSAGE_IS_EMPTY_TEMPLATE, Service.PutRequest.class.getName()) + ); + } + requestObserver.onNext(request); + } + + public Service.PatchResponse complete() { + requestObserver.onCompleted(); + + while (isNull(responseObserver.getResponse())) { + WaitUtil.sleep(DEFAULT_POLL_INTERVAL); + } + + return responseObserver.getResponse(); + } + + @Getter + private static class PatchResponseCallback implements StreamObserver { + private Service.PatchResponse response; + + @Override + public void onNext(Service.PatchResponse patchResponse) { + this.response = patchResponse; + } + + @Override + public void onError(Throwable throwable) { + throw new ProcessFrostFSException(throwable); + } + + @Override + public void onCompleted() { + } + } +} diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/RangeReader.java b/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/RangeReader.java new file mode 100644 index 0000000..75c4c64 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/RangeReader.java @@ -0,0 +1,25 @@ +package info.frostfs.sdk.services.impl.rwhelper; + +import frostfs.object.Service; +import info.frostfs.sdk.tools.Verifier; + +import java.util.Iterator; + +public class RangeReader { + public Iterator call; + + public RangeReader(Iterator call) { + this.call = call; + } + + public byte[] readChunk() { + if (!call.hasNext()) { + return null; + } + + var response = call.next(); + Verifier.checkResponse(response); + + return response.getBody().getChunk().toByteArray(); + } +} diff --git a/cryptography/pom.xml b/cryptography/pom.xml index ac56a86..e9e2c6b 100644 --- a/cryptography/pom.xml +++ b/cryptography/pom.xml @@ -6,7 +6,7 @@ info.frostfs.sdk frostfs-sdk-java - 0.3.0 + ${revision} cryptography @@ -21,7 +21,7 @@ info.frostfs.sdk exceptions - 0.3.0 + ${revision} com.google.protobuf diff --git a/exceptions/pom.xml b/exceptions/pom.xml index 4cb8bcc..c3b4546 100644 --- a/exceptions/pom.xml +++ b/exceptions/pom.xml @@ -6,7 +6,7 @@ info.frostfs.sdk frostfs-sdk-java - 0.3.0 + ${revision} exceptions diff --git a/models/pom.xml b/models/pom.xml index 32cade4..e913463 100644 --- a/models/pom.xml +++ b/models/pom.xml @@ -6,7 +6,7 @@ info.frostfs.sdk frostfs-sdk-java - 0.3.0 + ${revision} models @@ -21,17 +21,17 @@ info.frostfs.sdk cryptography - 0.3.0 + ${revision} info.frostfs.sdk protos - 0.3.0 + ${revision} info.frostfs.sdk exceptions - 0.3.0 + ${revision} diff --git a/models/src/main/java/info/frostfs/sdk/constants/AppConst.java b/models/src/main/java/info/frostfs/sdk/constants/AppConst.java index 35d64c6..feb1ca5 100644 --- a/models/src/main/java/info/frostfs/sdk/constants/AppConst.java +++ b/models/src/main/java/info/frostfs/sdk/constants/AppConst.java @@ -13,6 +13,7 @@ public class AppConst { public static final int SHA256_HASH_LENGTH = 32; public static final int UUID_BYTE_ARRAY_LENGTH = 16; public static final int DEFAULT_GRPC_TIMEOUT = 5; + public static final long DEFAULT_POLL_INTERVAL = 10; private AppConst() { } diff --git a/models/src/main/java/info/frostfs/sdk/dto/object/patch/Address.java b/models/src/main/java/info/frostfs/sdk/dto/object/patch/Address.java new file mode 100644 index 0000000..ae30880 --- /dev/null +++ b/models/src/main/java/info/frostfs/sdk/dto/object/patch/Address.java @@ -0,0 +1,13 @@ +package info.frostfs.sdk.dto.object.patch; + +import info.frostfs.sdk.dto.container.ContainerId; +import info.frostfs.sdk.dto.object.ObjectId; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class Address { + private final ObjectId objectId; + private final ContainerId containerId; +} diff --git a/models/src/main/java/info/frostfs/sdk/dto/object/patch/Range.java b/models/src/main/java/info/frostfs/sdk/dto/object/patch/Range.java new file mode 100644 index 0000000..67a56aa --- /dev/null +++ b/models/src/main/java/info/frostfs/sdk/dto/object/patch/Range.java @@ -0,0 +1,11 @@ +package info.frostfs.sdk.dto.object.patch; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class Range { + private long offset; + private long length; +} diff --git a/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectAttributeMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectAttributeMapper.java index 687aa86..f29c284 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectAttributeMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectAttributeMapper.java @@ -2,6 +2,10 @@ package info.frostfs.sdk.mappers.object; import frostfs.object.Types; import info.frostfs.sdk.dto.object.ObjectAttribute; +import org.apache.commons.collections4.CollectionUtils; + +import java.util.List; +import java.util.stream.Collectors; import static java.util.Objects.isNull; @@ -10,6 +14,14 @@ public class ObjectAttributeMapper { private ObjectAttributeMapper() { } + public static List toGrpcMessages(List attributes) { + if (CollectionUtils.isEmpty(attributes)) { + return null; + } + + return attributes.stream().map(ObjectAttributeMapper::toGrpcMessage).collect(Collectors.toList()); + } + public static Types.Header.Attribute toGrpcMessage(ObjectAttribute attribute) { if (isNull(attribute)) { return null; diff --git a/models/src/main/java/info/frostfs/sdk/mappers/object/patch/AddressMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/object/patch/AddressMapper.java new file mode 100644 index 0000000..1510b7a --- /dev/null +++ b/models/src/main/java/info/frostfs/sdk/mappers/object/patch/AddressMapper.java @@ -0,0 +1,25 @@ +package info.frostfs.sdk.mappers.object.patch; + +import frostfs.refs.Types; +import info.frostfs.sdk.dto.object.patch.Address; +import info.frostfs.sdk.mappers.container.ContainerIdMapper; +import info.frostfs.sdk.mappers.object.ObjectIdMapper; + +import static java.util.Objects.isNull; + +public class AddressMapper { + + private AddressMapper() { + } + + public static Types.Address toGrpcMessage(Address address) { + if (isNull(address)) { + return null; + } + + return Types.Address.newBuilder() + .setContainerId(ContainerIdMapper.toGrpcMessage(address.getContainerId())) + .setObjectId(ObjectIdMapper.toGrpcMessage(address.getObjectId())) + .build(); + } +} diff --git a/models/src/main/java/info/frostfs/sdk/mappers/object/patch/RangeMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/object/patch/RangeMapper.java new file mode 100644 index 0000000..f428545 --- /dev/null +++ b/models/src/main/java/info/frostfs/sdk/mappers/object/patch/RangeMapper.java @@ -0,0 +1,35 @@ +package info.frostfs.sdk.mappers.object.patch; + +import frostfs.object.Service; +import info.frostfs.sdk.dto.object.patch.Range; +import org.apache.commons.collections4.CollectionUtils; + +import java.util.List; +import java.util.stream.Collectors; + +import static java.util.Objects.isNull; + +public class RangeMapper { + + private RangeMapper() { + } + + public static List toGrpcMessages(List ranges) { + if (CollectionUtils.isEmpty(ranges)) { + return null; + } + + return ranges.stream().map(RangeMapper::toGrpcMessage).collect(Collectors.toList()); + } + + public static Service.Range toGrpcMessage(Range range) { + if (isNull(range)) { + return null; + } + + return Service.Range.newBuilder() + .setOffset(range.getOffset()) + .setLength(range.getLength()) + .build(); + } +} diff --git a/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectAttributeMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectAttributeMapperTest.java index 4ae2902..160af7a 100644 --- a/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectAttributeMapperTest.java +++ b/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectAttributeMapperTest.java @@ -4,10 +4,39 @@ import frostfs.object.Types; import info.frostfs.sdk.dto.object.ObjectAttribute; import org.junit.jupiter.api.Test; +import java.util.Arrays; +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.*; public class ObjectAttributeMapperTest { + @Test + void toGrpcMessages_success() { + //Given + var objectAttribute1 = new ObjectAttribute("key1", "value1"); + var objectAttribute2 = new ObjectAttribute("key2", "value2"); + + //When + var result = ObjectAttributeMapper.toGrpcMessages(Arrays.asList(objectAttribute1, objectAttribute2)); + + //Then + assertNotNull(result); + assertThat(result).isNotNull().hasSize(2); + assertEquals(objectAttribute1.getKey(), result.get(0).getKey()); + assertEquals(objectAttribute1.getValue(), result.get(0).getValue()); + assertEquals(objectAttribute2.getKey(), result.get(1).getKey()); + assertEquals(objectAttribute2.getValue(), result.get(1).getValue()); + } + + @Test + void toGrpcMessages_null() { + //When + Then + assertNull(ObjectAttributeMapper.toGrpcMessages(null)); + assertNull(ObjectAttributeMapper.toGrpcMessages(Collections.emptyList())); + } + @Test void toGrpcMessage_success() { //Given diff --git a/models/src/test/java/info/frostfs/sdk/mappers/object/patch/AddressMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/object/patch/AddressMapperTest.java new file mode 100644 index 0000000..b53ff87 --- /dev/null +++ b/models/src/test/java/info/frostfs/sdk/mappers/object/patch/AddressMapperTest.java @@ -0,0 +1,39 @@ +package info.frostfs.sdk.mappers.object.patch; + +import info.frostfs.sdk.dto.container.ContainerId; +import info.frostfs.sdk.dto.object.ObjectId; +import info.frostfs.sdk.dto.object.patch.Address; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class AddressMapperTest { + + @Test + void toGrpcMessage_success() { + //Given + var objectId = new ObjectId("85orCLKSu3X1jGiTFmwmTUsBU88RBARNwuRwrEy5pyww"); + var containerId = new ContainerId("EQGx2QeYHJb53uRwYGzcQaW191sZpdNrjutk6veUSV2R"); + var address = new Address(objectId, containerId); + + //When + var result = AddressMapper.toGrpcMessage(address); + + //Then + assertNotNull(result); + assertEquals( + address.getContainerId().getValue(), + new ContainerId(result.getContainerId().getValue().toByteArray()).getValue() + ); + assertEquals( + address.getObjectId().getValue(), + new ObjectId(result.getObjectId().getValue().toByteArray()).getValue() + ); + } + + @Test + void toGrpcMessage_null() { + //When + Then + assertNull(AddressMapper.toGrpcMessage(null)); + } +} diff --git a/models/src/test/java/info/frostfs/sdk/mappers/object/patch/RangeMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/object/patch/RangeMapperTest.java new file mode 100644 index 0000000..771a9ac --- /dev/null +++ b/models/src/test/java/info/frostfs/sdk/mappers/object/patch/RangeMapperTest.java @@ -0,0 +1,58 @@ +package info.frostfs.sdk.mappers.object.patch; + +import info.frostfs.sdk.dto.object.patch.Range; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; + +public class RangeMapperTest { + + @Test + void toGrpcMessages_success() { + //Given + var range1 = new Range(1, 10); + var range2 = new Range(2, 20); + + //When + var result = RangeMapper.toGrpcMessages(Arrays.asList(range1, range2)); + + //Then + assertNotNull(result); + assertThat(result).isNotNull().hasSize(2); + assertEquals(range1.getOffset(), result.get(0).getOffset()); + assertEquals(range1.getLength(), result.get(0).getLength()); + assertEquals(range2.getOffset(), result.get(1).getOffset()); + assertEquals(range2.getLength(), result.get(1).getLength()); + } + + @Test + void toGrpcMessages_null() { + //When + Then + assertNull(RangeMapper.toGrpcMessages(null)); + assertNull(RangeMapper.toGrpcMessages(Collections.emptyList())); + } + + @Test + void toGrpcMessage_success() { + //Given + var range = new Range(1, 10); + + //When + var result = RangeMapper.toGrpcMessage(range); + + //Then + assertNotNull(result); + assertEquals(range.getOffset(), result.getOffset()); + assertEquals(range.getLength(), result.getLength()); + } + + @Test + void toGrpcMessage_null() { + //When + Then + assertNull(RangeMapper.toGrpcMessage(null)); + } +} diff --git a/pom.xml b/pom.xml index 72eb9eb..d390c12 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ info.frostfs.sdk frostfs-sdk-java - 0.3.0 + ${revision} pom client @@ -17,6 +17,8 @@ + 0.4.0 + 11 11 UTF-8 diff --git a/protos/pom.xml b/protos/pom.xml index d3a2b3b..f5ea90a 100644 --- a/protos/pom.xml +++ b/protos/pom.xml @@ -6,7 +6,7 @@ info.frostfs.sdk frostfs-sdk-java - 0.3.0 + ${revision} protos From 532db56f1be778732ecf80ec588d3e2bddc5728d Mon Sep 17 00:00:00 2001 From: Ori Bruk Date: Tue, 11 Feb 2025 17:14:30 +0300 Subject: [PATCH 30/38] [#39] Fix loading large objects in chunks Signed-off-by: Ori Bruk --- .gitignore | 1 + CHANGELOG.md | 8 ++++++ .../sdk/services/impl/ObjectClientImpl.java | 6 +++-- .../sdk/services/impl/ObjectToolsImpl.java | 5 +++- pom.xml | 26 ++++++++++++++++++- 5 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 CHANGELOG.md diff --git a/.gitignore b/.gitignore index c3f0616..82d73ff 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ target/ !.mvn/wrapper/maven-wrapper.jar !**/src/main/**/target/ !**/src/test/**/target/ +**/.flattened-pom.xml ### IntelliJ IDEA ### .idea/modules.xml diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..7bcf58d --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,8 @@ +# Changelog + +## [0.5.0] - 2025-02-11 + +### Fixed +- Loading large objects in chunks. +- .gitignore +- pom revision \ No newline at end of file diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/ObjectClientImpl.java b/client/src/main/java/info/frostfs/sdk/services/impl/ObjectClientImpl.java index 266b630..25f6251 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/ObjectClientImpl.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/ObjectClientImpl.java @@ -166,8 +166,10 @@ public class ObjectClientImpl extends ContextAccessor implements ObjectClient { // send the last part and create linkObject if (CollectionUtils.isNotEmpty(sentObjectIds)) { - var largeObjectHeader = - new ObjectHeader(header.getContainerId(), ObjectType.REGULAR, attributes, fullLength, null); + var largeObjectHeader = new ObjectHeader( + header.getContainerId(), ObjectType.REGULAR, attributes, fullLength, header.getVersion() + ); + largeObjectHeader.setOwnerId(header.getOwnerId()); split.setParentHeader(largeObjectHeader); diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/ObjectToolsImpl.java b/client/src/main/java/info/frostfs/sdk/services/impl/ObjectToolsImpl.java index abe6927..f0f4e1c 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/ObjectToolsImpl.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/ObjectToolsImpl.java @@ -105,7 +105,10 @@ public class ObjectToolsImpl extends ContextAccessor implements ToolsClient { split.setParent(ObjectIdMapper.toModel(parentObjectId)); } - grpcSplit.setPrevious(ObjectIdMapper.toGrpcMessage(split.getPrevious())).build(); + if (nonNull(split.getPrevious())) { + grpcSplit.setPrevious(ObjectIdMapper.toGrpcMessage(split.getPrevious())).build(); + } + return grpcHeader.toBuilder().setSplit(grpcSplit.build()).build(); } diff --git a/pom.xml b/pom.xml index d390c12..313d679 100644 --- a/pom.xml +++ b/pom.xml @@ -17,7 +17,7 @@ - 0.4.0 + 0.5.0 11 11 @@ -128,6 +128,30 @@ + + org.codehaus.mojo + flatten-maven-plugin + 1.0.0 + + true + + + + flatten + process-resources + + flatten + + + + flatten.clean + clean + + clean + + + + \ No newline at end of file From 3861eb0dc2c81816c7c8248b655b8d77787a2ed8 Mon Sep 17 00:00:00 2001 From: Ori Bruk Date: Thu, 13 Feb 2025 20:25:29 +0300 Subject: [PATCH 31/38] [#41] Add APE rule serializer Signed-off-by: Ori Bruk --- CHANGELOG.md | 5 + .../java/info/frostfs/sdk/FrostFSClient.java | 3 +- .../info/frostfs/sdk/constants/RuleConst.java | 27 ++ .../jdo/parameters/ape/PrmApeChainAdd.java | 2 +- .../jdo/parameters/ape/PrmApeChainRemove.java | 8 +- .../main/java/info/frostfs/sdk/pool/Pool.java | 3 +- .../sdk/services/ApeManagerClient.java | 4 +- .../services/impl/ApeManagerClientImpl.java | 13 +- .../frostfs/sdk/tools/MarshalFunction.java | 5 + .../frostfs/sdk/tools/RuleSerializer.java | 256 ++++++++++++++++++ .../java/info/frostfs/sdk/tools/Verifier.java | 4 + .../sdk/services/ApeManagerClientTest.java | 49 +++- .../frostfs/sdk/constants/ErrorConst.java | 7 + .../info/frostfs/sdk/dto/ape/Actions.java | 15 + .../java/info/frostfs/sdk/dto/ape/Chain.java | 17 ++ .../info/frostfs/sdk/dto/ape/Condition.java | 19 ++ .../info/frostfs/sdk/dto/ape/Resources.java | 15 + .../java/info/frostfs/sdk/dto/ape/Rule.java | 27 ++ .../info/frostfs/sdk/dto/chain/Chain.java | 10 - .../sdk/dto/response/ResponseStatus.java | 10 +- .../frostfs/sdk/enums/ConditionKindType.java | 13 + .../info/frostfs/sdk/enums/ConditionType.java | 36 +++ .../info/frostfs/sdk/enums/RuleMatchType.java | 16 ++ .../info/frostfs/sdk/enums/RuleStatus.java | 15 + .../sdk/mappers/chain/ChainMapper.java | 31 --- .../response/ResponseStatusMapper.java | 9 +- .../sdk/mappers/chain/ChainMapperTest.java | 64 ----- pom.xml | 2 +- 28 files changed, 542 insertions(+), 143 deletions(-) create mode 100644 client/src/main/java/info/frostfs/sdk/constants/RuleConst.java create mode 100644 client/src/main/java/info/frostfs/sdk/tools/MarshalFunction.java create mode 100644 client/src/main/java/info/frostfs/sdk/tools/RuleSerializer.java create mode 100644 models/src/main/java/info/frostfs/sdk/dto/ape/Actions.java create mode 100644 models/src/main/java/info/frostfs/sdk/dto/ape/Chain.java create mode 100644 models/src/main/java/info/frostfs/sdk/dto/ape/Condition.java create mode 100644 models/src/main/java/info/frostfs/sdk/dto/ape/Resources.java create mode 100644 models/src/main/java/info/frostfs/sdk/dto/ape/Rule.java delete mode 100644 models/src/main/java/info/frostfs/sdk/dto/chain/Chain.java create mode 100644 models/src/main/java/info/frostfs/sdk/enums/ConditionKindType.java create mode 100644 models/src/main/java/info/frostfs/sdk/enums/ConditionType.java create mode 100644 models/src/main/java/info/frostfs/sdk/enums/RuleMatchType.java create mode 100644 models/src/main/java/info/frostfs/sdk/enums/RuleStatus.java delete mode 100644 models/src/main/java/info/frostfs/sdk/mappers/chain/ChainMapper.java delete mode 100644 models/src/test/java/info/frostfs/sdk/mappers/chain/ChainMapperTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 7bcf58d..033855d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## [0.6.0] - 2025-02-13 + +### Added +- APE rules serializer + ## [0.5.0] - 2025-02-11 ### Fixed diff --git a/client/src/main/java/info/frostfs/sdk/FrostFSClient.java b/client/src/main/java/info/frostfs/sdk/FrostFSClient.java index e6704ed..0026285 100644 --- a/client/src/main/java/info/frostfs/sdk/FrostFSClient.java +++ b/client/src/main/java/info/frostfs/sdk/FrostFSClient.java @@ -1,7 +1,6 @@ package info.frostfs.sdk; import frostfs.accounting.Types; -import info.frostfs.sdk.dto.chain.Chain; import info.frostfs.sdk.dto.container.Container; import info.frostfs.sdk.dto.container.ContainerId; import info.frostfs.sdk.dto.netmap.NetmapSnapshot; @@ -194,7 +193,7 @@ public class FrostFSClient implements CommonClient { } @Override - public List listChains(PrmApeChainList args, CallContext ctx) { + public List listChains(PrmApeChainList args, CallContext ctx) { return apeManagerClient.listChains(args, ctx); } diff --git a/client/src/main/java/info/frostfs/sdk/constants/RuleConst.java b/client/src/main/java/info/frostfs/sdk/constants/RuleConst.java new file mode 100644 index 0000000..78e656f --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/constants/RuleConst.java @@ -0,0 +1,27 @@ +package info.frostfs.sdk.constants; + +public class RuleConst { + public static final byte VERSION = 0; + + public static final int BYTE_SIZE = 1; + public static final int U_INT_8_SIZE = BYTE_SIZE; + public static final int BOOL_SIZE = BYTE_SIZE; + + public static final long NULL_SLICE = -1L; + public static final int NULL_SLICE_SIZE = 1; + + public static final byte BYTE_TRUE = 1; + public static final byte BYTE_FALSE = 0; + + // maxSliceLen taken from + // https://github.com/neo-project/neo/blob/38218bbee5bbe8b33cd8f9453465a19381c9a547/src/Neo/IO/Helper.cs#L77 + public static final int MAX_SLICE_LENGTH = 0x1000000; + + public static final int CHAIN_MARSHAL_VERSION = 0; + + public static final long OFFSET128 = 0x80; + public static final long UNSIGNED_SERIALIZE_SIZE = 7; + + private RuleConst() { + } +} diff --git a/client/src/main/java/info/frostfs/sdk/jdo/parameters/ape/PrmApeChainAdd.java b/client/src/main/java/info/frostfs/sdk/jdo/parameters/ape/PrmApeChainAdd.java index 4692ac2..e7ae05c 100644 --- a/client/src/main/java/info/frostfs/sdk/jdo/parameters/ape/PrmApeChainAdd.java +++ b/client/src/main/java/info/frostfs/sdk/jdo/parameters/ape/PrmApeChainAdd.java @@ -1,7 +1,7 @@ package info.frostfs.sdk.jdo.parameters.ape; import info.frostfs.sdk.annotations.NotNull; -import info.frostfs.sdk.dto.chain.Chain; +import info.frostfs.sdk.dto.ape.Chain; import info.frostfs.sdk.dto.chain.ChainTarget; import lombok.AllArgsConstructor; import lombok.Builder; diff --git a/client/src/main/java/info/frostfs/sdk/jdo/parameters/ape/PrmApeChainRemove.java b/client/src/main/java/info/frostfs/sdk/jdo/parameters/ape/PrmApeChainRemove.java index 93f53d3..00bc2e9 100644 --- a/client/src/main/java/info/frostfs/sdk/jdo/parameters/ape/PrmApeChainRemove.java +++ b/client/src/main/java/info/frostfs/sdk/jdo/parameters/ape/PrmApeChainRemove.java @@ -1,7 +1,6 @@ package info.frostfs.sdk.jdo.parameters.ape; import info.frostfs.sdk.annotations.NotNull; -import info.frostfs.sdk.dto.chain.Chain; import info.frostfs.sdk.dto.chain.ChainTarget; import lombok.AllArgsConstructor; import lombok.Builder; @@ -14,14 +13,15 @@ import java.util.Map; @AllArgsConstructor public class PrmApeChainRemove { @NotNull - private Chain chain; + private byte[] chainId; + @NotNull private ChainTarget chainTarget; private Map xHeaders; - public PrmApeChainRemove(Chain chain, ChainTarget chainTarget) { - this.chain = chain; + public PrmApeChainRemove(byte[] chainId, ChainTarget chainTarget) { + this.chainId = chainId; this.chainTarget = chainTarget; } } diff --git a/client/src/main/java/info/frostfs/sdk/pool/Pool.java b/client/src/main/java/info/frostfs/sdk/pool/Pool.java index 45b818e..d8bac59 100644 --- a/client/src/main/java/info/frostfs/sdk/pool/Pool.java +++ b/client/src/main/java/info/frostfs/sdk/pool/Pool.java @@ -1,7 +1,6 @@ package info.frostfs.sdk.pool; import frostfs.refs.Types; -import info.frostfs.sdk.dto.chain.Chain; import info.frostfs.sdk.dto.container.Container; import info.frostfs.sdk.dto.container.ContainerId; import info.frostfs.sdk.dto.netmap.NetmapSnapshot; @@ -515,7 +514,7 @@ public class Pool implements CommonClient { } @Override - public List listChains(PrmApeChainList args, CallContext ctx) { + public List listChains(PrmApeChainList args, CallContext ctx) { ClientWrapper client = connection(); return client.getClient().listChains(args, ctx); } diff --git a/client/src/main/java/info/frostfs/sdk/services/ApeManagerClient.java b/client/src/main/java/info/frostfs/sdk/services/ApeManagerClient.java index 521d8af..965e458 100644 --- a/client/src/main/java/info/frostfs/sdk/services/ApeManagerClient.java +++ b/client/src/main/java/info/frostfs/sdk/services/ApeManagerClient.java @@ -1,6 +1,6 @@ package info.frostfs.sdk.services; -import info.frostfs.sdk.dto.chain.Chain; +import frostfs.ape.Types; import info.frostfs.sdk.jdo.parameters.CallContext; import info.frostfs.sdk.jdo.parameters.ape.PrmApeChainAdd; import info.frostfs.sdk.jdo.parameters.ape.PrmApeChainList; @@ -13,5 +13,5 @@ public interface ApeManagerClient { void removeChain(PrmApeChainRemove args, CallContext ctx); - List listChains(PrmApeChainList args, CallContext ctx); + List listChains(PrmApeChainList args, CallContext ctx); } diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/ApeManagerClientImpl.java b/client/src/main/java/info/frostfs/sdk/services/impl/ApeManagerClientImpl.java index 7843d65..cd81060 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/ApeManagerClientImpl.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/ApeManagerClientImpl.java @@ -4,18 +4,17 @@ import com.google.protobuf.ByteString; import frostfs.ape.Types; import frostfs.apemanager.APEManagerServiceGrpc; import frostfs.apemanager.Service; -import info.frostfs.sdk.dto.chain.Chain; import info.frostfs.sdk.jdo.ClientEnvironment; import info.frostfs.sdk.jdo.parameters.CallContext; import info.frostfs.sdk.jdo.parameters.ape.PrmApeChainAdd; import info.frostfs.sdk.jdo.parameters.ape.PrmApeChainList; import info.frostfs.sdk.jdo.parameters.ape.PrmApeChainRemove; -import info.frostfs.sdk.mappers.chain.ChainMapper; import info.frostfs.sdk.mappers.chain.ChainTargetMapper; import info.frostfs.sdk.services.ApeManagerClient; import info.frostfs.sdk.services.ContextAccessor; import info.frostfs.sdk.tools.RequestConstructor; import info.frostfs.sdk.tools.RequestSigner; +import info.frostfs.sdk.tools.RuleSerializer; import info.frostfs.sdk.tools.Verifier; import java.util.List; @@ -58,7 +57,7 @@ public class ApeManagerClientImpl extends ContextAccessor implements ApeManagerC } @Override - public List listChains(PrmApeChainList args, CallContext ctx) { + public List listChains(PrmApeChainList args, CallContext ctx) { validate(args); var request = createListChainsRequest(args); @@ -68,12 +67,14 @@ public class ApeManagerClientImpl extends ContextAccessor implements ApeManagerC Verifier.checkResponse(response); - return ChainMapper.toModels(response.getBody().getChainsList()); + return response.getBody().getChainsList(); } private Service.AddChainRequest createAddChainRequest(PrmApeChainAdd args) { + var raw = RuleSerializer.serialize(args.getChain()); + var chainGrpc = Types.Chain.newBuilder() - .setRaw(ByteString.copyFrom(args.getChain().getRaw())) + .setRaw(ByteString.copyFrom(raw)) .build(); var body = Service.AddChainRequest.Body.newBuilder() .setChain(chainGrpc) @@ -90,7 +91,7 @@ public class ApeManagerClientImpl extends ContextAccessor implements ApeManagerC private Service.RemoveChainRequest createRemoveChainRequest(PrmApeChainRemove args) { var body = Service.RemoveChainRequest.Body.newBuilder() - .setChainId(ByteString.copyFrom(args.getChain().getRaw())) + .setChainId(ByteString.copyFrom(args.getChainId())) .setTarget(ChainTargetMapper.toGrpcMessage(args.getChainTarget())) .build(); var request = Service.RemoveChainRequest.newBuilder() diff --git a/client/src/main/java/info/frostfs/sdk/tools/MarshalFunction.java b/client/src/main/java/info/frostfs/sdk/tools/MarshalFunction.java new file mode 100644 index 0000000..accd7b1 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/tools/MarshalFunction.java @@ -0,0 +1,5 @@ +package info.frostfs.sdk.tools; + +public interface MarshalFunction { + int marshal(byte[] buf, int offset, T t); +} diff --git a/client/src/main/java/info/frostfs/sdk/tools/RuleSerializer.java b/client/src/main/java/info/frostfs/sdk/tools/RuleSerializer.java new file mode 100644 index 0000000..097708d --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/tools/RuleSerializer.java @@ -0,0 +1,256 @@ +package info.frostfs.sdk.tools; + +import info.frostfs.sdk.dto.ape.*; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; +import org.apache.commons.lang3.StringUtils; + +import java.nio.charset.StandardCharsets; +import java.util.function.Function; + +import static info.frostfs.sdk.constants.ErrorConst.*; +import static info.frostfs.sdk.constants.RuleConst.*; +import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; + +public class RuleSerializer { + private RuleSerializer() { + } + + public static byte[] serialize(Chain chain) { + int s = U_INT_8_SIZE // Marshaller version + + U_INT_8_SIZE // Chain version + + sliceSize(chain.getId(), b -> BYTE_SIZE) + + sliceSize(chain.getRules(), RuleSerializer::ruleSize) + + U_INT_8_SIZE; // MatchType + + byte[] buf = new byte[s]; + + int offset = uInt8Marshal(buf, 0, VERSION); + offset = uInt8Marshal(buf, offset, (byte) CHAIN_MARSHAL_VERSION); + offset = sliceMarshal(buf, offset, chain.getId(), RuleSerializer::byteMarshal); + offset = sliceMarshal(buf, offset, chain.getRules(), RuleSerializer::marshalRule); + offset = uInt8Marshal(buf, offset, (byte) chain.getMatchType().value); + + verifyMarshal(buf, offset); + + return buf; + } + + private static int sliceSize(T[] slice, Function sizeOf) { + if (isNull(slice)) { + return NULL_SLICE_SIZE; + } + + // Assuming int64Size is the size of the slice + int size = int64Size(slice.length); + for (T v : slice) { + size += sizeOf.apply(v); + } + + return size; + } + + /* + * https://cs.opensource.google/go/go/+/master:src/encoding/binary/varint.go;l=92;drc=dac9b9ddbd5160c5f4552410f5f828 + * 1bd5eed38c + * + * and + * + * https://cs.opensource.google/go/go/+/master:src/encoding/binary/varint.go;l=41;drc=dac9b9ddbd5160c5f4552410f5f828 + * 1bd5eed38c + * */ + private static int int64Size(long value) { + long ux = value << 1; + if (value < 0) { + ux = ~ux; + } + + int size = 0; + while (ux >= OFFSET128) { + size++; + ux >>>= UNSIGNED_SERIALIZE_SIZE; + } + + return size + 1; + } + + private static int stringSize(String s) { + int len = nonNull(s) ? s.length() : 0; + return int64Size(len) + len; + } + + private static int actionsSize(Actions action) { + return BOOL_SIZE // Inverted + + (nonNull(action) ? sliceSize(action.getNames(), RuleSerializer::stringSize) : 0); + } + + private static int resourcesSize(Resources resource) { + return BOOL_SIZE // Inverted + + (nonNull(resource) ? sliceSize(resource.getNames(), RuleSerializer::stringSize) : 0); + } + + private static int conditionSize(Condition condition) { + if (isNull(condition)) { + throw new ValidationFrostFSException(String.format(REQUIRED_FIELD_TEMPLATE, Rule.class.getName())); + } + + return BYTE_SIZE // Op + + BYTE_SIZE // Object + + stringSize(condition.getKey()) + + stringSize(condition.getValue()); + } + + private static int ruleSize(Rule rule) { + if (isNull(rule)) { + throw new ValidationFrostFSException(String.format(REQUIRED_FIELD_TEMPLATE, Rule.class.getName())); + } + + return BYTE_SIZE // Status + + actionsSize(rule.getActions()) + + resourcesSize(rule.getResources()) + + BOOL_SIZE // Any + + sliceSize(rule.getConditions(), RuleSerializer::conditionSize); + } + + private static int uInt8Marshal(byte[] buf, int offset, byte value) { + if (buf.length - offset < 1) { + throw new ValidationFrostFSException( + String.format(BYTES_ARE_OVER_FOR_SERIALIZE_TEMPLATE, Byte.class.getName(), 1) + ); + } + + buf[offset] = value; + + return offset + 1; + } + + private static int byteMarshal(byte[] buf, int offset, byte value) { + return uInt8Marshal(buf, offset, value); + } + + // putVarInt encodes an int64 into buf and returns the number of bytes written. + private static int putVarInt(byte[] buf, int offset, long x) { + long ux = x << 1; + if (x < 0) { + ux = ~ux; + } + + return putUVarInt(buf, offset, ux); + } + + private static int putUVarInt(byte[] buf, int offset, long x) { + while (x >= OFFSET128) { + buf[offset] = (byte) (x | OFFSET128); + x >>>= UNSIGNED_SERIALIZE_SIZE; + offset++; + } + buf[offset] = (byte) x; + return offset + 1; + } + + private static int int64Marshal(byte[] buf, int offset, long v) { + var size = int64Size(v); + if (buf.length - offset < size) { + throw new ValidationFrostFSException( + String.format(BYTES_ARE_OVER_FOR_SERIALIZE_TEMPLATE, Long.class.getName(), size) + ); + } + + return putVarInt(buf, offset, v); + } + + private static int sliceMarshal(byte[] buf, int offset, T[] slice, MarshalFunction marshalT) { + if (isNull(slice)) { + return int64Marshal(buf, offset, NULL_SLICE); + } + + if (slice.length > MAX_SLICE_LENGTH) { + throw new ValidationFrostFSException(String.format(SLICE_IS_TOO_BIG_TEMPLATE, slice.length)); + } + + offset = int64Marshal(buf, offset, slice.length); + for (T v : slice) { + offset = marshalT.marshal(buf, offset, v); + } + + return offset; + } + + private static int boolMarshal(byte[] buf, int offset, boolean value) { + return uInt8Marshal(buf, offset, value ? BYTE_TRUE : BYTE_FALSE); + } + + private static int stringMarshal(byte[] buf, int offset, String value) { + if (StringUtils.isBlank(value)) { + throw new ValidationFrostFSException(STRING_IS_BLANK); + } + + if (value.length() > MAX_SLICE_LENGTH) { + throw new ValidationFrostFSException(String.format(STRING_IS_TOO_BIG_TEMPLATE, value.length())); + } + + if (buf.length - offset < int64Size(value.length()) + value.length()) { + throw new ValidationFrostFSException( + String.format(BYTES_ARE_OVER_FOR_SERIALIZE_TEMPLATE, String.class.getName(), value.length()) + ); + } + + offset = int64Marshal(buf, offset, value.length()); + if (value.isEmpty()) { + return offset; + } + + byte[] stringBytes = value.getBytes(StandardCharsets.UTF_8); + + // Copy exactly value.length() bytes as in the original code + System.arraycopy(stringBytes, 0, buf, offset, value.length()); + return offset + value.length(); + } + + private static int marshalActions(byte[] buf, int offset, Actions action) { + if (isNull(action)) { + throw new ValidationFrostFSException(String.format(REQUIRED_FIELD_TEMPLATE, Actions.class.getName())); + } + + offset = boolMarshal(buf, offset, action.isInverted()); + return sliceMarshal(buf, offset, action.getNames(), RuleSerializer::stringMarshal); + } + + private static int marshalCondition(byte[] buf, int offset, Condition condition) { + if (isNull(condition)) { + throw new ValidationFrostFSException(String.format(REQUIRED_FIELD_TEMPLATE, Condition.class.getName())); + } + + offset = byteMarshal(buf, offset, (byte) condition.getOp().value); + offset = byteMarshal(buf, offset, (byte) condition.getKind().value); + offset = stringMarshal(buf, offset, condition.getKey()); + return stringMarshal(buf, offset, condition.getValue()); + } + + private static int marshalRule(byte[] buf, int offset, Rule rule) { + if (isNull(rule)) { + throw new ValidationFrostFSException(String.format(REQUIRED_FIELD_TEMPLATE, Rule.class.getName())); + } + + offset = byteMarshal(buf, offset, (byte) rule.getStatus().value); + offset = marshalActions(buf, offset, rule.getActions()); + offset = marshalResources(buf, offset, rule.getResources()); + offset = boolMarshal(buf, offset, rule.isAny()); + return sliceMarshal(buf, offset, rule.getConditions(), RuleSerializer::marshalCondition); + } + + private static int marshalResources(byte[] buf, int offset, Resources resources) { + if (isNull(resources)) { + throw new ValidationFrostFSException(String.format(REQUIRED_FIELD_TEMPLATE, Resources.class.getName())); + } + + offset = boolMarshal(buf, offset, resources.isInverted()); + return sliceMarshal(buf, offset, resources.getNames(), RuleSerializer::stringMarshal); + } + + private static void verifyMarshal(byte[] buf, int lastOffset) { + if (buf.length != lastOffset) { + throw new ValidationFrostFSException(MARSHAL_SIZE_DIFFERS); + } + } +} diff --git a/client/src/main/java/info/frostfs/sdk/tools/Verifier.java b/client/src/main/java/info/frostfs/sdk/tools/Verifier.java index f4dbde3..8a5e968 100644 --- a/client/src/main/java/info/frostfs/sdk/tools/Verifier.java +++ b/client/src/main/java/info/frostfs/sdk/tools/Verifier.java @@ -78,6 +78,10 @@ public class Verifier { } var metaHeader = (Types.ResponseMetaHeader) MessageHelper.getField(response, META_HEADER_FIELD_NAME); + if (isNull(metaHeader) || metaHeader.getSerializedSize() == 0) { + return; + } + var status = ResponseStatusMapper.toModel(metaHeader.getStatus()); if (!status.isSuccess()) { throw new ResponseFrostFSException(status); diff --git a/client/src/test/java/info/frostfs/sdk/services/ApeManagerClientTest.java b/client/src/test/java/info/frostfs/sdk/services/ApeManagerClientTest.java index fa09315..5e62c74 100644 --- a/client/src/test/java/info/frostfs/sdk/services/ApeManagerClientTest.java +++ b/client/src/test/java/info/frostfs/sdk/services/ApeManagerClientTest.java @@ -2,10 +2,9 @@ package info.frostfs.sdk.services; import frostfs.apemanager.APEManagerServiceGrpc; import frostfs.apemanager.Service; -import info.frostfs.sdk.FileUtils; -import info.frostfs.sdk.dto.chain.Chain; +import info.frostfs.sdk.dto.ape.*; import info.frostfs.sdk.dto.chain.ChainTarget; -import info.frostfs.sdk.enums.TargetType; +import info.frostfs.sdk.enums.*; import info.frostfs.sdk.exceptions.ValidationFrostFSException; import info.frostfs.sdk.jdo.ClientEnvironment; import info.frostfs.sdk.jdo.parameters.CallContext; @@ -18,6 +17,7 @@ import info.frostfs.sdk.tools.RequestConstructor; import info.frostfs.sdk.tools.RequestSigner; import info.frostfs.sdk.tools.Verifier; import io.grpc.Channel; +import org.apache.commons.lang3.ArrayUtils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -30,7 +30,8 @@ import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import java.lang.reflect.Field; -import java.util.stream.Collectors; +import java.nio.charset.StandardCharsets; +import java.util.Base64; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -39,6 +40,9 @@ import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) class ApeManagerClientTest { + private static final String CHAIN_BASE64 = + "AAAaY2hhaW4taWQtdGVzdAIAAAISR2V0T2JqZWN0AAIebmF0aXZlOm9iamVjdC8qAAIAABREZXBhcnRtZW50BEhSAA=="; + private ApeManagerClientImpl apeManagerClient; @Mock @@ -107,7 +111,9 @@ class ApeManagerClientTest { assertThat(result).containsOnly(response.getBody().getChainId().toByteArray()); var request = captor.getValue(); - assertThat(request.getBody().getChain().getRaw().toByteArray()).containsOnly(chain.getRaw()); + assertEquals( + Base64.getEncoder().encodeToString(request.getBody().getChain().getRaw().toByteArray()), CHAIN_BASE64) + ; assertEquals(chainTarget.getName(), request.getBody().getTarget().getName()); assertEquals(chainTarget.getType().value, request.getBody().getTarget().getType().getNumber()); } @@ -133,7 +139,7 @@ class ApeManagerClientTest { //Given Chain chain = generateChain(); ChainTarget chainTarget = generateChainTarget(); - PrmApeChainRemove params = new PrmApeChainRemove(chain, chainTarget); + PrmApeChainRemove params = new PrmApeChainRemove(Base64.getDecoder().decode(CHAIN_BASE64), chainTarget); var response = ApeManagerGenerator.generateRemoveChainResponse(); @@ -156,7 +162,7 @@ class ApeManagerClientTest { verifierMock.verify(() -> Verifier.checkResponse(response), times(1)); var request = captor.getValue(); - assertThat(request.getBody().getChainId().toByteArray()).containsOnly(chain.getRaw()); + assertThat(request.getBody().getChainId().toByteArray()).containsOnly(Base64.getDecoder().decode(CHAIN_BASE64)); assertEquals(chainTarget.getName(), request.getBody().getTarget().getName()); assertEquals(chainTarget.getType().value, request.getBody().getTarget().getType().getNumber()); } @@ -167,7 +173,7 @@ class ApeManagerClientTest { Chain chain = generateChain(); ChainTarget chainTarget = generateChainTarget(); PrmApeChainRemove params1 = new PrmApeChainRemove(null, chainTarget); - PrmApeChainRemove params2 = new PrmApeChainRemove(chain, null); + PrmApeChainRemove params2 = new PrmApeChainRemove(Base64.getDecoder().decode(CHAIN_BASE64), null); PrmApeChainRemove params3 = new PrmApeChainRemove(null, null); //When + Then @@ -202,11 +208,8 @@ class ApeManagerClientTest { ); verifierMock.verify(() -> Verifier.checkResponse(response), times(1)); - var actual = result.stream().map(Chain::getRaw).collect(Collectors.toList()); - var expected = response.getBody().getChainsList().stream() - .map(chain -> chain.getRaw().toByteArray()) - .collect(Collectors.toList()); - assertThat(actual).hasSize(10).containsAll(expected); + var expected = response.getBody().getChainsList(); + assertThat(result).hasSize(10).containsAll(expected); var request = captor.getValue(); assertEquals(chainTarget.getName(), request.getBody().getTarget().getName()); @@ -221,8 +224,24 @@ class ApeManagerClientTest { } private Chain generateChain() { - byte[] chainRaw = FileUtils.resourceToBytes("test_chain_raw.json"); - return new Chain(chainRaw); + var resources = new Resources(false, new String[]{"native:object/*"}); + var actions = new Actions(false, new String[]{"GetObject"}); + var condition = new Condition( + ConditionType.COND_STRING_EQUALS, ConditionKindType.RESOURCE, "Department", "HR" + ); + + var rule = new Rule(); + rule.setStatus(RuleStatus.ALLOW); + rule.setResources(resources); + rule.setActions(actions); + rule.setAny(false); + rule.setConditions(new Condition[]{condition}); + + var chain = new Chain(); + chain.setId(ArrayUtils.toObject("chain-id-test".getBytes(StandardCharsets.UTF_8))); + chain.setRules(new Rule[]{rule}); + chain.setMatchType(RuleMatchType.DENY_PRIORITY); + return chain; } private ChainTarget generateChainTarget() { diff --git a/exceptions/src/main/java/info/frostfs/sdk/constants/ErrorConst.java b/exceptions/src/main/java/info/frostfs/sdk/constants/ErrorConst.java index 62c3eb8..629943e 100644 --- a/exceptions/src/main/java/info/frostfs/sdk/constants/ErrorConst.java +++ b/exceptions/src/main/java/info/frostfs/sdk/constants/ErrorConst.java @@ -2,6 +2,7 @@ package info.frostfs.sdk.constants; public class ErrorConst { public static final String OBJECT_IS_NULL = "object must not be null"; + public static final String STRING_IS_BLANK = "string must not be blank"; public static final String INPUT_PARAM_IS_MISSING = "input parameter is not present"; public static final String SOME_PARAM_IS_MISSING = "one of the input parameters is not present"; public static final String PARAM_IS_MISSING_TEMPLATE = "param %s is not present"; @@ -51,6 +52,12 @@ public class ErrorConst { public static final String FIELDS_DELIMITER_COMMA = ", "; public static final String FIELDS_DELIMITER_OR = " or "; + public static final String MARSHAL_SIZE_DIFFERS = "actual data size differs from expected"; + public static final String BYTES_ARE_OVER_FOR_SERIALIZE_TEMPLATE = + "not enough bytes left to serialize value of type %s with length=%s"; + public static final String SLICE_IS_TOO_BIG_TEMPLATE = "slice size is too big=%s"; + public static final String STRING_IS_TOO_BIG_TEMPLATE = "string size is too big=%s"; + private ErrorConst() { } } diff --git a/models/src/main/java/info/frostfs/sdk/dto/ape/Actions.java b/models/src/main/java/info/frostfs/sdk/dto/ape/Actions.java new file mode 100644 index 0000000..83415a4 --- /dev/null +++ b/models/src/main/java/info/frostfs/sdk/dto/ape/Actions.java @@ -0,0 +1,15 @@ +package info.frostfs.sdk.dto.ape; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class Actions { + private boolean inverted; + private String[] names; +} diff --git a/models/src/main/java/info/frostfs/sdk/dto/ape/Chain.java b/models/src/main/java/info/frostfs/sdk/dto/ape/Chain.java new file mode 100644 index 0000000..0e8385f --- /dev/null +++ b/models/src/main/java/info/frostfs/sdk/dto/ape/Chain.java @@ -0,0 +1,17 @@ +package info.frostfs.sdk.dto.ape; + +import info.frostfs.sdk.enums.RuleMatchType; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class Chain { + private Byte[] id; + private Rule[] rules; + private RuleMatchType matchType; +} diff --git a/models/src/main/java/info/frostfs/sdk/dto/ape/Condition.java b/models/src/main/java/info/frostfs/sdk/dto/ape/Condition.java new file mode 100644 index 0000000..8334f94 --- /dev/null +++ b/models/src/main/java/info/frostfs/sdk/dto/ape/Condition.java @@ -0,0 +1,19 @@ +package info.frostfs.sdk.dto.ape; + +import info.frostfs.sdk.enums.ConditionKindType; +import info.frostfs.sdk.enums.ConditionType; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class Condition { + private ConditionType op; + private ConditionKindType kind; + private String key; + private String value; +} diff --git a/models/src/main/java/info/frostfs/sdk/dto/ape/Resources.java b/models/src/main/java/info/frostfs/sdk/dto/ape/Resources.java new file mode 100644 index 0000000..017ed4a --- /dev/null +++ b/models/src/main/java/info/frostfs/sdk/dto/ape/Resources.java @@ -0,0 +1,15 @@ +package info.frostfs.sdk.dto.ape; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class Resources { + private boolean inverted; + private String[] names; +} diff --git a/models/src/main/java/info/frostfs/sdk/dto/ape/Rule.java b/models/src/main/java/info/frostfs/sdk/dto/ape/Rule.java new file mode 100644 index 0000000..14e462f --- /dev/null +++ b/models/src/main/java/info/frostfs/sdk/dto/ape/Rule.java @@ -0,0 +1,27 @@ +package info.frostfs.sdk.dto.ape; + +import info.frostfs.sdk.enums.RuleStatus; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class Rule { + private RuleStatus status; + + // Actions the operation is applied to. + private Actions actions; + + // List of the resources the operation is applied to. + private Resources resources; + + // True if individual conditions must be combined with the logical OR. + // By default, AND is used, so _each_ condition must pass. + private boolean any; + + private Condition[] conditions; +} diff --git a/models/src/main/java/info/frostfs/sdk/dto/chain/Chain.java b/models/src/main/java/info/frostfs/sdk/dto/chain/Chain.java deleted file mode 100644 index 984b163..0000000 --- a/models/src/main/java/info/frostfs/sdk/dto/chain/Chain.java +++ /dev/null @@ -1,10 +0,0 @@ -package info.frostfs.sdk.dto.chain; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -@Getter -@AllArgsConstructor -public class Chain { - private final byte[] raw; -} diff --git a/models/src/main/java/info/frostfs/sdk/dto/response/ResponseStatus.java b/models/src/main/java/info/frostfs/sdk/dto/response/ResponseStatus.java index af69f98..02602f1 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/response/ResponseStatus.java +++ b/models/src/main/java/info/frostfs/sdk/dto/response/ResponseStatus.java @@ -3,19 +3,21 @@ package info.frostfs.sdk.dto.response; import info.frostfs.sdk.enums.StatusCode; import lombok.Getter; import lombok.Setter; +import org.apache.commons.lang3.StringUtils; import static info.frostfs.sdk.constants.FieldConst.EMPTY_STRING; -import static java.util.Objects.isNull; @Getter @Setter public class ResponseStatus { private StatusCode code; private String message; + private String details; - public ResponseStatus(StatusCode code, String message) { + public ResponseStatus(StatusCode code, String message, String details) { this.code = code; - this.message = isNull(message) ? EMPTY_STRING : message; + this.message = StringUtils.isBlank(message) ? EMPTY_STRING : message; + this.details = StringUtils.isBlank(details) ? EMPTY_STRING : details; } public ResponseStatus(StatusCode code) { @@ -25,7 +27,7 @@ public class ResponseStatus { @Override public String toString() { - return String.format("Response status: %s. Message: %s.", code, message); + return String.format("Response status: %s. Message: %s. Details: %s", code, message, details); } public boolean isSuccess() { diff --git a/models/src/main/java/info/frostfs/sdk/enums/ConditionKindType.java b/models/src/main/java/info/frostfs/sdk/enums/ConditionKindType.java new file mode 100644 index 0000000..a437254 --- /dev/null +++ b/models/src/main/java/info/frostfs/sdk/enums/ConditionKindType.java @@ -0,0 +1,13 @@ +package info.frostfs.sdk.enums; + +public enum ConditionKindType { + RESOURCE(0), + REQUEST(1), + ; + + public final int value; + + ConditionKindType(int value) { + this.value = value; + } +} diff --git a/models/src/main/java/info/frostfs/sdk/enums/ConditionType.java b/models/src/main/java/info/frostfs/sdk/enums/ConditionType.java new file mode 100644 index 0000000..b23417c --- /dev/null +++ b/models/src/main/java/info/frostfs/sdk/enums/ConditionType.java @@ -0,0 +1,36 @@ +package info.frostfs.sdk.enums; + +public enum ConditionType { + COND_STRING_EQUALS(0), + COND_STRING_NOT_EQUALS(1), + COND_STRING_EQUALS_IGNORE_CASE(2), + COND_STRING_NOT_EQUALS_IGNORE_CASE(3), + + COND_STRING_LIKE(4), + COND_STRING_NOT_LIKE(5), + + COND_STRING_LESS_THAN(6), + COND_STRING_LESS_THAN_EQUALS(7), + COND_STRING_GREATER_THAN(8), + COND_STRING_GREATER_THAN_EQUALS(9), + + COND_NUMERIC_EQUALS(10), + COND_NUMERIC_NOT_EQUALS(11), + + COND_NUMERIC_LESS_THAN(12), + COND_NUMERIC_LESS_THAN_EQUALS(13), + COND_NUMERIC_GREATER_THAN(14), + COND_NUMERIC_GREATER_THAN_EQUALS(15), + + COND_SLICE_CONTAINS(16), + + COND_IP_ADDRESS(17), + COND_NOT_IP_ADDRESS(18), + ; + + public final int value; + + ConditionType(int value) { + this.value = value; + } +} diff --git a/models/src/main/java/info/frostfs/sdk/enums/RuleMatchType.java b/models/src/main/java/info/frostfs/sdk/enums/RuleMatchType.java new file mode 100644 index 0000000..bfacaf6 --- /dev/null +++ b/models/src/main/java/info/frostfs/sdk/enums/RuleMatchType.java @@ -0,0 +1,16 @@ +package info.frostfs.sdk.enums; + +public enum RuleMatchType { + // DENY_PRIORITY rejects the request if any `Deny` is specified. + DENY_PRIORITY(0), + + // FIRST_MATCH returns the first rule action matched to the request. + FIRST_MATCH(1), + ; + + public final int value; + + RuleMatchType(int value) { + this.value = value; + } +} diff --git a/models/src/main/java/info/frostfs/sdk/enums/RuleStatus.java b/models/src/main/java/info/frostfs/sdk/enums/RuleStatus.java new file mode 100644 index 0000000..b2f6cf1 --- /dev/null +++ b/models/src/main/java/info/frostfs/sdk/enums/RuleStatus.java @@ -0,0 +1,15 @@ +package info.frostfs.sdk.enums; + +public enum RuleStatus { + ALLOW(0), + NO_RULE_FOUND(1), + ACCESS_DENIED(2), + QUOTA_LIMIT_REACHED(3), + ; + + public final int value; + + RuleStatus(int value) { + this.value = value; + } +} diff --git a/models/src/main/java/info/frostfs/sdk/mappers/chain/ChainMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/chain/ChainMapper.java deleted file mode 100644 index 20197f7..0000000 --- a/models/src/main/java/info/frostfs/sdk/mappers/chain/ChainMapper.java +++ /dev/null @@ -1,31 +0,0 @@ -package info.frostfs.sdk.mappers.chain; - -import frostfs.ape.Types; -import info.frostfs.sdk.dto.chain.Chain; -import org.apache.commons.collections4.CollectionUtils; - -import java.util.List; -import java.util.stream.Collectors; - -import static java.util.Objects.isNull; - -public class ChainMapper { - private ChainMapper() { - } - - public static List toModels(List chains) { - if (CollectionUtils.isEmpty(chains)) { - return null; - } - - return chains.stream().map(ChainMapper::toModel).collect(Collectors.toList()); - } - - public static Chain toModel(Types.Chain chain) { - if (isNull(chain) || chain.getSerializedSize() == 0) { - return null; - } - - return new Chain(chain.getRaw().toByteArray()); - } -} diff --git a/models/src/main/java/info/frostfs/sdk/mappers/response/ResponseStatusMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/response/ResponseStatusMapper.java index cb935dd..59c1563 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/response/ResponseStatusMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/response/ResponseStatusMapper.java @@ -5,6 +5,9 @@ import info.frostfs.sdk.dto.response.ResponseStatus; import info.frostfs.sdk.enums.StatusCode; import info.frostfs.sdk.exceptions.ProcessFrostFSException; +import java.util.stream.Collectors; + +import static info.frostfs.sdk.constants.ErrorConst.FIELDS_DELIMITER_COMMA; import static info.frostfs.sdk.constants.ErrorConst.UNKNOWN_ENUM_VALUE_TEMPLATE; import static java.util.Objects.isNull; @@ -24,6 +27,10 @@ public class ResponseStatusMapper { ); } - return new ResponseStatus(statusCode, status.getMessage()); + var stringDetails = status.getDetailsList().stream() + .map(t -> t.getValue().toStringUtf8()) + .collect(Collectors.toList()); + + return new ResponseStatus(statusCode, status.getMessage(), String.join(FIELDS_DELIMITER_COMMA, stringDetails)); } } diff --git a/models/src/test/java/info/frostfs/sdk/mappers/chain/ChainMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/chain/ChainMapperTest.java deleted file mode 100644 index 7d2c4b6..0000000 --- a/models/src/test/java/info/frostfs/sdk/mappers/chain/ChainMapperTest.java +++ /dev/null @@ -1,64 +0,0 @@ -package info.frostfs.sdk.mappers.chain; - -import com.google.protobuf.ByteString; -import frostfs.ape.Types; -import org.junit.jupiter.api.Test; - -import java.util.Collections; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; - -public class ChainMapperTest { - - @Test - void toModels_success() { - //Given - var chain1 = Types.Chain.newBuilder() - .setRaw(ByteString.copyFrom(new byte[]{1, 2, 3, 4, 5})) - .build(); - var chain2 = Types.Chain.newBuilder() - .setRaw(ByteString.copyFrom(new byte[]{6, 7, 8, 9, 10})) - .build(); - - //When - var result = ChainMapper.toModels(List.of(chain1, chain2)); - - //Then - assertNotNull(result); - assertThat(result).hasSize(2); - assertThat(result.get(0).getRaw()).containsOnly(chain1.getRaw().toByteArray()); - assertThat(result.get(1).getRaw()).containsOnly(chain2.getRaw().toByteArray()); - } - - @Test - void toModels_null() { - //When + Then - assertNull(ChainMapper.toModels(null)); - assertNull(ChainMapper.toModels(Collections.emptyList())); - } - - @Test - void toModel_success() { - //Given - var chain = Types.Chain.newBuilder() - .setRaw(ByteString.copyFrom(new byte[]{1, 2, 3, 4, 5})) - .build(); - - //When - var result = ChainMapper.toModel(chain); - - //Then - assertNotNull(result); - assertThat(result.getRaw()).containsOnly(chain.getRaw().toByteArray()); - } - - @Test - void toModel_null() { - //When + Then - assertNull(ChainMapper.toModel(null)); - assertNull(ChainMapper.toModel(Types.Chain.getDefaultInstance())); - } -} diff --git a/pom.xml b/pom.xml index 313d679..2f42332 100644 --- a/pom.xml +++ b/pom.xml @@ -17,7 +17,7 @@ - 0.5.0 + 0.6.0 11 11 From fe7d2968b88473da58e718c1a91da1b623317cb0 Mon Sep 17 00:00:00 2001 From: Ori Bruk Date: Thu, 20 Feb 2025 14:32:20 +0300 Subject: [PATCH 32/38] [#43] Expanding the parameters for creating a container Signed-off-by: Ori Bruk --- CHANGELOG.md | 10 +- README.md | 4 +- .../services/impl/ContainerClientImpl.java | 12 +- .../sdk/services/impl/ObjectClientImpl.java | 11 +- .../services/impl/rwhelper/ObjectWriter.java | 2 + .../frostfs/sdk/tools/RequestConstructor.java | 1 - .../frostfs/sdk/dto/container/Container.java | 9 +- .../info/frostfs/sdk/dto/netmap/Filter.java | 15 ++ .../sdk/dto/netmap/PlacementPolicy.java | 11 + .../info/frostfs/sdk/dto/netmap/Selector.java | 15 ++ .../frostfs/sdk/enums/FilterOperation.java | 40 ++++ .../frostfs/sdk/enums/SelectorClause.java | 32 +++ .../mappers/container/ContainerIdMapper.java | 1 - .../mappers/container/ContainerMapper.java | 41 ++-- .../sdk/mappers/netmap/FilterMapper.java | 85 +++++++ .../mappers/netmap/NetmapSnapshotMapper.java | 1 - .../mappers/netmap/PlacementPolicyMapper.java | 23 +- .../sdk/mappers/netmap/ReplicaMapper.java | 30 ++- .../sdk/mappers/netmap/SelectorMapper.java | 85 +++++++ .../sdk/mappers/netmap/VersionMapper.java | 1 - .../mappers/object/ObjectAttributeMapper.java | 4 +- .../mappers/object/ObjectFrostFSMapper.java | 1 - .../sdk/mappers/object/ObjectIdMapper.java | 1 - .../sdk/mappers/object/OwnerIdMapper.java | 10 +- .../mappers/object/patch/AddressMapper.java | 1 - .../sdk/mappers/object/patch/RangeMapper.java | 4 +- .../container/ContainerMapperTest.java | 60 ++++- .../sdk/mappers/netmap/FilterMapperTest.java | 209 ++++++++++++++++++ .../netmap/PlacementPolicyMapperTest.java | 5 +- .../sdk/mappers/netmap/ReplicaMapperTest.java | 3 +- .../mappers/netmap/SelectorMapperTest.java | 191 ++++++++++++++++ .../object/ObjectAttributeMapperTest.java | 5 +- .../mappers/object/patch/RangeMapperTest.java | 5 +- pom.xml | 2 +- 34 files changed, 855 insertions(+), 75 deletions(-) create mode 100644 models/src/main/java/info/frostfs/sdk/dto/netmap/Filter.java create mode 100644 models/src/main/java/info/frostfs/sdk/dto/netmap/Selector.java create mode 100644 models/src/main/java/info/frostfs/sdk/enums/FilterOperation.java create mode 100644 models/src/main/java/info/frostfs/sdk/enums/SelectorClause.java create mode 100644 models/src/main/java/info/frostfs/sdk/mappers/netmap/FilterMapper.java create mode 100644 models/src/main/java/info/frostfs/sdk/mappers/netmap/SelectorMapper.java create mode 100644 models/src/test/java/info/frostfs/sdk/mappers/netmap/FilterMapperTest.java create mode 100644 models/src/test/java/info/frostfs/sdk/mappers/netmap/SelectorMapperTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 033855d..d9e6a86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## [0.7.0] - 2025-02-20 + +### Added +- Expanding the parameters for creating a container + +### Fixed +- Creating a session for working with objects + ## [0.6.0] - 2025-02-13 ### Added @@ -8,6 +16,6 @@ ## [0.5.0] - 2025-02-11 ### Fixed -- Loading large objects in chunks. +- Loading large objects in chunks - .gitignore - pom revision \ No newline at end of file diff --git a/README.md b/README.md index 03f013b..fd1efff 100644 --- a/README.md +++ b/README.md @@ -41,8 +41,8 @@ public class ContainerExample { FrostFSClient frostFSClient = new FrostFSClient(clientSettings); // Create container - var placementPolicy = new PlacementPolicy(new Replica[]{new Replica(1)}, true); - var prmContainerCreate = new PrmContainerCreate(new Container(BasicAcl.PUBLIC_RW, placementPolicy)); + var placementPolicy = new PlacementPolicy(new Replica[]{new Replica(1)}, true, 0); + var prmContainerCreate = new PrmContainerCreate(new Container(placementPolicy)); var containerId = frostFSClient.createContainer(prmContainerCreate, callContext); // Get container diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/ContainerClientImpl.java b/client/src/main/java/info/frostfs/sdk/services/impl/ContainerClientImpl.java index e6c5d31..7e32ca1 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/ContainerClientImpl.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/ContainerClientImpl.java @@ -186,11 +186,15 @@ public class ContainerClientImpl extends ContextAccessor implements ContainerCli private Service.PutRequest createPutRequest(PrmContainerCreate args, CallContext ctx) { syncContainerWithNetwork(args.getContainer(), ctx); - var container = ContainerMapper.toGrpcMessage(args.getContainer()).toBuilder() - .setOwnerId(OwnerIdMapper.toGrpcMessage(getContext().getOwnerId())) - .setVersion(VersionMapper.toGrpcMessage(getContext().getVersion())) - .build(); + var builder = ContainerMapper.toGrpcMessage(args.getContainer()); + if (!builder.hasOwnerId()) { + builder.setOwnerId(OwnerIdMapper.toGrpcMessage(getContext().getOwnerId())); + } + if (!builder.hasVersion()) { + builder.setVersion(VersionMapper.toGrpcMessage(getContext().getVersion())); + } + var container = builder.build(); var body = Service.PutRequest.Body.newBuilder() .setContainer(container) .setSignature(RequestSigner.signRFC6979(getContext().getKey(), container)) diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/ObjectClientImpl.java b/client/src/main/java/info/frostfs/sdk/services/impl/ObjectClientImpl.java index 25f6251..46b2905 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/ObjectClientImpl.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/ObjectClientImpl.java @@ -35,7 +35,6 @@ import java.io.InputStream; import java.util.ArrayList; import java.util.List; -import static info.frostfs.sdk.Helper.getSha256; import static info.frostfs.sdk.constants.ErrorConst.PROTO_MESSAGE_IS_EMPTY_TEMPLATE; import static info.frostfs.sdk.tools.RequestSigner.sign; import static info.frostfs.sdk.utils.DeadLineUtil.deadLineAfter; @@ -335,6 +334,7 @@ public class ObjectClientImpl extends ContextAccessor implements ObjectClient { .setBody(body) .clearVerifyHeader(); + RequestConstructor.addMetaHeader(chunkRequest, args.getXHeaders()); sign(chunkRequest, getContext().getKey()); stream.write(chunkRequest.build()); @@ -359,9 +359,7 @@ public class ObjectClientImpl extends ContextAccessor implements ObjectClient { grpcHeader = objectToolsImpl.updateSplitValues(grpcHeader, header.getSplit()); } - var oid = Types.ObjectID.newBuilder().setValue(getSha256(grpcHeader)).build(); - - var initRequest = createInitPutRequest(oid, grpcHeader, args, ctx); + var initRequest = createInitPutRequest(grpcHeader, args, ctx); return putObjectInit(initRequest, ctx); } @@ -527,13 +525,11 @@ public class ObjectClientImpl extends ContextAccessor implements ObjectClient { return request.build(); } - private Service.PutRequest createInitPutRequest(Types.ObjectID oid, - frostfs.object.Types.Header header, + private Service.PutRequest createInitPutRequest(frostfs.object.Types.Header header, PrmObjectPutBase args, CallContext ctx) { var address = Types.Address.newBuilder() .setContainerId(header.getContainerId()) - .setObjectId(oid) .build(); var init = Service.PutRequest.Body.Init.newBuilder() .setHeader(header) @@ -563,7 +559,6 @@ public class ObjectClientImpl extends ContextAccessor implements ObjectClient { CallContext ctx) { var address = Types.Address.newBuilder() .setContainerId(grpcObject.getHeader().getContainerId()) - .setObjectId(grpcObject.getObjectId()) .build(); var body = Service.PutSingleRequest.Body.newBuilder() .setObject(grpcObject) diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectWriter.java b/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectWriter.java index 76327f5..9650dbf 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectWriter.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/rwhelper/ObjectWriter.java @@ -5,6 +5,7 @@ import frostfs.object.Service; import info.frostfs.sdk.dto.object.ObjectId; import info.frostfs.sdk.jdo.ClientEnvironment; import info.frostfs.sdk.jdo.parameters.object.PrmObjectPutBase; +import info.frostfs.sdk.tools.RequestConstructor; import info.frostfs.sdk.tools.Verifier; import lombok.AllArgsConstructor; import lombok.Getter; @@ -27,6 +28,7 @@ public class ObjectWriter { .setBody(body) .clearVerifyHeader(); + RequestConstructor.addMetaHeader(chunkRequest, args.getXHeaders()); sign(chunkRequest, environment.getKey()); streamer.write(chunkRequest.build()); diff --git a/client/src/main/java/info/frostfs/sdk/tools/RequestConstructor.java b/client/src/main/java/info/frostfs/sdk/tools/RequestConstructor.java index af2150d..17a011c 100644 --- a/client/src/main/java/info/frostfs/sdk/tools/RequestConstructor.java +++ b/client/src/main/java/info/frostfs/sdk/tools/RequestConstructor.java @@ -86,7 +86,6 @@ public class RequestConstructor { .build(); var body = protoToken.getBody().toBuilder() .setObject(ctx) - .setSessionKey(ByteString.copyFrom(key.getPublicKeyByte())) .build(); return protoToken.toBuilder() diff --git a/models/src/main/java/info/frostfs/sdk/dto/container/Container.java b/models/src/main/java/info/frostfs/sdk/dto/container/Container.java index 6f04b2e..b9a0ea4 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/container/Container.java +++ b/models/src/main/java/info/frostfs/sdk/dto/container/Container.java @@ -2,7 +2,8 @@ package info.frostfs.sdk.dto.container; import info.frostfs.sdk.dto.netmap.PlacementPolicy; import info.frostfs.sdk.dto.netmap.Version; -import info.frostfs.sdk.enums.BasicAcl; +import info.frostfs.sdk.dto.object.OwnerId; +import lombok.AllArgsConstructor; import lombok.Getter; import lombok.Setter; @@ -12,16 +13,16 @@ import java.util.UUID; @Getter @Setter +@AllArgsConstructor public class Container { private UUID nonce; - private BasicAcl basicAcl; private PlacementPolicy placementPolicy; private Version version; + private OwnerId ownerId; private Map attributes = new HashMap<>(); - public Container(BasicAcl basicAcl, PlacementPolicy placementPolicy) { + public Container(PlacementPolicy placementPolicy) { this.nonce = UUID.randomUUID(); - this.basicAcl = basicAcl; this.placementPolicy = placementPolicy; } } diff --git a/models/src/main/java/info/frostfs/sdk/dto/netmap/Filter.java b/models/src/main/java/info/frostfs/sdk/dto/netmap/Filter.java new file mode 100644 index 0000000..fe809fe --- /dev/null +++ b/models/src/main/java/info/frostfs/sdk/dto/netmap/Filter.java @@ -0,0 +1,15 @@ +package info.frostfs.sdk.dto.netmap; + +import info.frostfs.sdk.enums.FilterOperation; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class Filter { + private final String name; + private final String key; + private final FilterOperation operation; + private final String value; + private final Filter[] filters; +} diff --git a/models/src/main/java/info/frostfs/sdk/dto/netmap/PlacementPolicy.java b/models/src/main/java/info/frostfs/sdk/dto/netmap/PlacementPolicy.java index 0356dea..d25808b 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/netmap/PlacementPolicy.java +++ b/models/src/main/java/info/frostfs/sdk/dto/netmap/PlacementPolicy.java @@ -8,4 +8,15 @@ import lombok.Getter; public class PlacementPolicy { private final Replica[] replicas; private final boolean unique; + private final int backupFactory; + private final Filter[] filters; + private final Selector[] selectors; + + public PlacementPolicy(Replica[] replicas, boolean unique, int backupFactory) { + this.replicas = replicas; + this.unique = unique; + this.backupFactory = backupFactory; + this.filters = null; + this.selectors = null; + } } diff --git a/models/src/main/java/info/frostfs/sdk/dto/netmap/Selector.java b/models/src/main/java/info/frostfs/sdk/dto/netmap/Selector.java new file mode 100644 index 0000000..71197a1 --- /dev/null +++ b/models/src/main/java/info/frostfs/sdk/dto/netmap/Selector.java @@ -0,0 +1,15 @@ +package info.frostfs.sdk.dto.netmap; + +import info.frostfs.sdk.enums.SelectorClause; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class Selector { + private final String name; + private final int count; + private final SelectorClause clause; + private final String attribute; + private final String filter; +} diff --git a/models/src/main/java/info/frostfs/sdk/enums/FilterOperation.java b/models/src/main/java/info/frostfs/sdk/enums/FilterOperation.java new file mode 100644 index 0000000..f49b0f2 --- /dev/null +++ b/models/src/main/java/info/frostfs/sdk/enums/FilterOperation.java @@ -0,0 +1,40 @@ +package info.frostfs.sdk.enums; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public enum FilterOperation { + OPERATION_UNSPECIFIED(0), + EQ(1), + NE(2), + GT(3), + GE(4), + LT(5), + LE(6), + OR(7), + AND(8), + NOT(9), + LIKE(10), + ; + + private static final Map ENUM_MAP_BY_VALUE; + + static { + Map map = new HashMap<>(); + for (FilterOperation nodeState : FilterOperation.values()) { + map.put(nodeState.value, nodeState); + } + ENUM_MAP_BY_VALUE = Collections.unmodifiableMap(map); + } + + public final int value; + + FilterOperation(int value) { + this.value = value; + } + + public static FilterOperation get(int value) { + return ENUM_MAP_BY_VALUE.get(value); + } +} diff --git a/models/src/main/java/info/frostfs/sdk/enums/SelectorClause.java b/models/src/main/java/info/frostfs/sdk/enums/SelectorClause.java new file mode 100644 index 0000000..b10ff0c --- /dev/null +++ b/models/src/main/java/info/frostfs/sdk/enums/SelectorClause.java @@ -0,0 +1,32 @@ +package info.frostfs.sdk.enums; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public enum SelectorClause { + CLAUSE_UNSPECIFIED(0), + SAME(1), + DISTINCT(2), + ; + + private static final Map ENUM_MAP_BY_VALUE; + + static { + Map map = new HashMap<>(); + for (SelectorClause nodeState : SelectorClause.values()) { + map.put(nodeState.value, nodeState); + } + ENUM_MAP_BY_VALUE = Collections.unmodifiableMap(map); + } + + public final int value; + + SelectorClause(int value) { + this.value = value; + } + + public static SelectorClause get(int value) { + return ENUM_MAP_BY_VALUE.get(value); + } +} diff --git a/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerIdMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerIdMapper.java index 26e941c..4daf0fc 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerIdMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerIdMapper.java @@ -7,7 +7,6 @@ import info.frostfs.sdk.dto.container.ContainerId; import static java.util.Objects.isNull; public class ContainerIdMapper { - private ContainerIdMapper() { } diff --git a/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerMapper.java index f0a8196..0571ff0 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerMapper.java @@ -7,8 +7,9 @@ import info.frostfs.sdk.enums.BasicAcl; import info.frostfs.sdk.exceptions.ProcessFrostFSException; import info.frostfs.sdk.mappers.netmap.PlacementPolicyMapper; import info.frostfs.sdk.mappers.netmap.VersionMapper; -import org.apache.commons.collections4.CollectionUtils; +import info.frostfs.sdk.mappers.object.OwnerIdMapper; +import java.util.Optional; import java.util.stream.Collectors; import static info.frostfs.sdk.UuidExtension.asBytes; @@ -20,16 +21,11 @@ public class ContainerMapper { private ContainerMapper() { } - public static Types.Container toGrpcMessage(Container container) { + public static Types.Container.Builder toGrpcMessage(Container container) { if (isNull(container)) { return null; } - var containerGrpc = Types.Container.newBuilder() - .setBasicAcl(container.getBasicAcl().value) - .setPlacementPolicy(PlacementPolicyMapper.toGrpcMessage(container.getPlacementPolicy())) - .setNonce(ByteString.copyFrom(asBytes(container.getNonce()))); - var attributes = container.getAttributes().entrySet().stream() .map(entry -> Types.Container.Attribute.newBuilder() @@ -38,9 +34,16 @@ public class ContainerMapper { .build() ) .collect(Collectors.toList()); - containerGrpc.addAllAttributes(attributes); - return containerGrpc.build(); + var containerGrpc = Types.Container.newBuilder() + .setPlacementPolicy(PlacementPolicyMapper.toGrpcMessage(container.getPlacementPolicy())) + .setNonce(ByteString.copyFrom(asBytes(container.getNonce()))) + .addAllAttributes(attributes); + + Optional.ofNullable(OwnerIdMapper.toGrpcMessage(container.getOwnerId())).ifPresent(containerGrpc::setOwnerId); + Optional.ofNullable(VersionMapper.toGrpcMessage(container.getVersion())).ifPresent(containerGrpc::setVersion); + + return containerGrpc; } public static Container toModel(Types.Container containerGrpc) { @@ -55,17 +58,15 @@ public class ContainerMapper { ); } + var attributes = containerGrpc.getAttributesList().stream() + .collect(Collectors.toMap(Types.Container.Attribute::getKey, Types.Container.Attribute::getValue)); - var container = new Container(basicAcl, PlacementPolicyMapper.toModel(containerGrpc.getPlacementPolicy())); - container.setNonce(asUuid(containerGrpc.getNonce().toByteArray())); - container.setVersion(VersionMapper.toModel(containerGrpc.getVersion())); - - if (CollectionUtils.isNotEmpty(containerGrpc.getAttributesList())) { - var attributes = containerGrpc.getAttributesList().stream() - .collect(Collectors.toMap(Types.Container.Attribute::getKey, Types.Container.Attribute::getValue)); - container.setAttributes(attributes); - } - - return container; + return new Container( + asUuid(containerGrpc.getNonce().toByteArray()), + PlacementPolicyMapper.toModel(containerGrpc.getPlacementPolicy()), + VersionMapper.toModel(containerGrpc.getVersion()), + OwnerIdMapper.toModel(containerGrpc.getOwnerId()), + attributes + ); } } diff --git a/models/src/main/java/info/frostfs/sdk/mappers/netmap/FilterMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/netmap/FilterMapper.java new file mode 100644 index 0000000..3f63b3b --- /dev/null +++ b/models/src/main/java/info/frostfs/sdk/mappers/netmap/FilterMapper.java @@ -0,0 +1,85 @@ +package info.frostfs.sdk.mappers.netmap; + +import frostfs.netmap.Types; +import info.frostfs.sdk.dto.netmap.Filter; +import info.frostfs.sdk.enums.FilterOperation; +import info.frostfs.sdk.exceptions.ProcessFrostFSException; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.ArrayUtils; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import static info.frostfs.sdk.constants.ErrorConst.INPUT_PARAM_IS_MISSING_TEMPLATE; +import static info.frostfs.sdk.constants.ErrorConst.UNKNOWN_ENUM_VALUE_TEMPLATE; +import static java.util.Objects.isNull; + +public class FilterMapper { + private FilterMapper() { + } + + public static List toGrpcMessages(Filter[] filters) { + if (ArrayUtils.isEmpty(filters)) { + return Collections.emptyList(); + } + + return Arrays.stream(filters).map(FilterMapper::toGrpcMessage).collect(Collectors.toList()); + } + + public static Types.Filter toGrpcMessage(Filter filter) { + if (isNull(filter)) { + throw new ValidationFrostFSException( + String.format(INPUT_PARAM_IS_MISSING_TEMPLATE, Filter.class.getName()) + ); + } + + var operation = Types.Operation.forNumber(filter.getOperation().value); + if (isNull(operation)) { + throw new ProcessFrostFSException(String.format( + UNKNOWN_ENUM_VALUE_TEMPLATE, + Types.Operation.class.getName(), + filter.getOperation().name() + )); + } + + return Types.Filter.newBuilder() + .setName(filter.getName()) + .setKey(filter.getKey()) + .setOp(operation) + .setValue(filter.getValue()) + .addAllFilters(toGrpcMessages(filter.getFilters())) + .build(); + } + + public static Filter[] toModels(List filters) { + if (CollectionUtils.isEmpty(filters)) { + return null; + } + + return filters.stream().map(FilterMapper::toModel).toArray(Filter[]::new); + } + + public static Filter toModel(Types.Filter filter) { + if (isNull(filter) || filter.getSerializedSize() == 0) { + return null; + } + + var operation = FilterOperation.get(filter.getOpValue()); + if (isNull(operation)) { + throw new ProcessFrostFSException( + String.format(UNKNOWN_ENUM_VALUE_TEMPLATE, FilterOperation.class.getName(), filter.getOp()) + ); + } + + return new Filter( + filter.getName(), + filter.getKey(), + operation, + filter.getValue(), + toModels(filter.getFiltersList()) + ); + } +} diff --git a/models/src/main/java/info/frostfs/sdk/mappers/netmap/NetmapSnapshotMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/netmap/NetmapSnapshotMapper.java index b7abf4e..7cad56b 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/netmap/NetmapSnapshotMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/netmap/NetmapSnapshotMapper.java @@ -8,7 +8,6 @@ import java.util.stream.Collectors; import static java.util.Objects.isNull; public class NetmapSnapshotMapper { - private NetmapSnapshotMapper() { } diff --git a/models/src/main/java/info/frostfs/sdk/mappers/netmap/PlacementPolicyMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/netmap/PlacementPolicyMapper.java index 22c65bb..03cdc5f 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/netmap/PlacementPolicyMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/netmap/PlacementPolicyMapper.java @@ -2,7 +2,6 @@ package info.frostfs.sdk.mappers.netmap; import frostfs.netmap.Types; import info.frostfs.sdk.dto.netmap.PlacementPolicy; -import info.frostfs.sdk.dto.netmap.Replica; import static java.util.Objects.isNull; @@ -15,14 +14,13 @@ public class PlacementPolicyMapper { return null; } - var pp = Types.PlacementPolicy.newBuilder() - .setUnique(placementPolicy.isUnique()); - - for (Replica replica : placementPolicy.getReplicas()) { - pp.addReplicas(ReplicaMapper.toGrpcMessage(replica)); - } - - return pp.build(); + return Types.PlacementPolicy.newBuilder() + .setUnique(placementPolicy.isUnique()) + .setContainerBackupFactor(placementPolicy.getBackupFactory()) + .addAllFilters(FilterMapper.toGrpcMessages(placementPolicy.getFilters())) + .addAllSelectors(SelectorMapper.toGrpcMessages(placementPolicy.getSelectors())) + .addAllReplicas(ReplicaMapper.toGrpcMessages(placementPolicy.getReplicas())) + .build(); } public static PlacementPolicy toModel(Types.PlacementPolicy placementPolicy) { @@ -31,8 +29,11 @@ public class PlacementPolicyMapper { } return new PlacementPolicy( - placementPolicy.getReplicasList().stream().map(ReplicaMapper::toModel).toArray(Replica[]::new), - placementPolicy.getUnique() + ReplicaMapper.toModels(placementPolicy.getReplicasList()), + placementPolicy.getUnique(), + placementPolicy.getContainerBackupFactor(), + FilterMapper.toModels(placementPolicy.getFiltersList()), + SelectorMapper.toModels(placementPolicy.getSelectorsList()) ); } } diff --git a/models/src/main/java/info/frostfs/sdk/mappers/netmap/ReplicaMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/netmap/ReplicaMapper.java index 6bb3939..959b9d6 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/netmap/ReplicaMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/netmap/ReplicaMapper.java @@ -2,17 +2,35 @@ package info.frostfs.sdk.mappers.netmap; import frostfs.netmap.Types; import info.frostfs.sdk.dto.netmap.Replica; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.ArrayUtils; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import static info.frostfs.sdk.constants.ErrorConst.INPUT_PARAM_IS_MISSING_TEMPLATE; import static java.util.Objects.isNull; public class ReplicaMapper { - private ReplicaMapper() { } + public static List toGrpcMessages(Replica[] replicas) { + if (ArrayUtils.isEmpty(replicas)) { + return Collections.emptyList(); + } + + return Arrays.stream(replicas).map(ReplicaMapper::toGrpcMessage).collect(Collectors.toList()); + } + public static Types.Replica toGrpcMessage(Replica replica) { if (isNull(replica)) { - return null; + throw new ValidationFrostFSException( + String.format(INPUT_PARAM_IS_MISSING_TEMPLATE, Replica.class.getName()) + ); } return Types.Replica.newBuilder() @@ -21,6 +39,14 @@ public class ReplicaMapper { .build(); } + public static Replica[] toModels(List filters) { + if (CollectionUtils.isEmpty(filters)) { + return null; + } + + return filters.stream().map(ReplicaMapper::toModel).toArray(Replica[]::new); + } + public static Replica toModel(Types.Replica replica) { if (isNull(replica) || replica.getSerializedSize() == 0) { return null; diff --git a/models/src/main/java/info/frostfs/sdk/mappers/netmap/SelectorMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/netmap/SelectorMapper.java new file mode 100644 index 0000000..8b9067d --- /dev/null +++ b/models/src/main/java/info/frostfs/sdk/mappers/netmap/SelectorMapper.java @@ -0,0 +1,85 @@ +package info.frostfs.sdk.mappers.netmap; + +import frostfs.netmap.Types; +import info.frostfs.sdk.dto.netmap.Selector; +import info.frostfs.sdk.enums.SelectorClause; +import info.frostfs.sdk.exceptions.ProcessFrostFSException; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.ArrayUtils; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import static info.frostfs.sdk.constants.ErrorConst.INPUT_PARAM_IS_MISSING_TEMPLATE; +import static info.frostfs.sdk.constants.ErrorConst.UNKNOWN_ENUM_VALUE_TEMPLATE; +import static java.util.Objects.isNull; + +public class SelectorMapper { + private SelectorMapper() { + } + + public static List toGrpcMessages(Selector[] selectors) { + if (ArrayUtils.isEmpty(selectors)) { + return Collections.emptyList(); + } + + return Arrays.stream(selectors).map(SelectorMapper::toGrpcMessage).collect(Collectors.toList()); + } + + public static Types.Selector toGrpcMessage(Selector selector) { + if (isNull(selector)) { + throw new ValidationFrostFSException( + String.format(INPUT_PARAM_IS_MISSING_TEMPLATE, Selector.class.getName()) + ); + } + + var clause = Types.Clause.forNumber(selector.getClause().value); + if (isNull(clause)) { + throw new ProcessFrostFSException(String.format( + UNKNOWN_ENUM_VALUE_TEMPLATE, + Types.Clause.class.getName(), + selector.getClause().name() + )); + } + + return Types.Selector.newBuilder() + .setName(selector.getName()) + .setCount(selector.getCount()) + .setClause(clause) + .setAttribute(selector.getAttribute()) + .setFilter(selector.getFilter()) + .build(); + } + + public static Selector[] toModels(List selectors) { + if (CollectionUtils.isEmpty(selectors)) { + return null; + } + + return selectors.stream().map(SelectorMapper::toModel).toArray(Selector[]::new); + } + + public static Selector toModel(Types.Selector selector) { + if (isNull(selector) || selector.getSerializedSize() == 0) { + return null; + } + + var clause = SelectorClause.get(selector.getClauseValue()); + if (isNull(clause)) { + throw new ProcessFrostFSException( + String.format(UNKNOWN_ENUM_VALUE_TEMPLATE, SelectorClause.class.getName(), selector.getClause()) + ); + } + + return new Selector( + selector.getName(), + selector.getCount(), + clause, + selector.getAttribute(), + selector.getFilter() + ); + } +} diff --git a/models/src/main/java/info/frostfs/sdk/mappers/netmap/VersionMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/netmap/VersionMapper.java index 386cb78..ecfdd57 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/netmap/VersionMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/netmap/VersionMapper.java @@ -6,7 +6,6 @@ import info.frostfs.sdk.dto.netmap.Version; import static java.util.Objects.isNull; public class VersionMapper { - private VersionMapper() { } diff --git a/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectAttributeMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectAttributeMapper.java index f29c284..c0c55a6 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectAttributeMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectAttributeMapper.java @@ -4,19 +4,19 @@ import frostfs.object.Types; import info.frostfs.sdk.dto.object.ObjectAttribute; import org.apache.commons.collections4.CollectionUtils; +import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import static java.util.Objects.isNull; public class ObjectAttributeMapper { - private ObjectAttributeMapper() { } public static List toGrpcMessages(List attributes) { if (CollectionUtils.isEmpty(attributes)) { - return null; + return Collections.emptyList(); } return attributes.stream().map(ObjectAttributeMapper::toGrpcMessage).collect(Collectors.toList()); diff --git a/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectFrostFSMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectFrostFSMapper.java index 228661b..2427a7b 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectFrostFSMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectFrostFSMapper.java @@ -7,7 +7,6 @@ import info.frostfs.sdk.dto.object.ObjectId; import static java.util.Objects.isNull; public class ObjectFrostFSMapper { - private ObjectFrostFSMapper() { } diff --git a/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectIdMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectIdMapper.java index 5c05d17..e095244 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectIdMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/object/ObjectIdMapper.java @@ -7,7 +7,6 @@ import info.frostfs.sdk.dto.object.ObjectId; import static java.util.Objects.isNull; public class ObjectIdMapper { - private ObjectIdMapper() { } diff --git a/models/src/main/java/info/frostfs/sdk/mappers/object/OwnerIdMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/object/OwnerIdMapper.java index 905f879..e1047b8 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/object/OwnerIdMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/object/OwnerIdMapper.java @@ -2,12 +2,12 @@ package info.frostfs.sdk.mappers.object; import com.google.protobuf.ByteString; import frostfs.refs.Types; +import info.frostfs.sdk.Base58; import info.frostfs.sdk.dto.object.OwnerId; import static java.util.Objects.isNull; public class OwnerIdMapper { - private OwnerIdMapper() { } @@ -20,4 +20,12 @@ public class OwnerIdMapper { .setValue(ByteString.copyFrom(ownerId.toHash())) .build(); } + + public static OwnerId toModel(Types.OwnerID ownerId) { + if (isNull(ownerId) || ownerId.getSerializedSize() == 0) { + return null; + } + + return new OwnerId(Base58.encode(ownerId.getValue().toByteArray())); + } } diff --git a/models/src/main/java/info/frostfs/sdk/mappers/object/patch/AddressMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/object/patch/AddressMapper.java index 1510b7a..1e34d62 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/object/patch/AddressMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/object/patch/AddressMapper.java @@ -8,7 +8,6 @@ import info.frostfs.sdk.mappers.object.ObjectIdMapper; import static java.util.Objects.isNull; public class AddressMapper { - private AddressMapper() { } diff --git a/models/src/main/java/info/frostfs/sdk/mappers/object/patch/RangeMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/object/patch/RangeMapper.java index f428545..0e3ebe5 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/object/patch/RangeMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/object/patch/RangeMapper.java @@ -4,19 +4,19 @@ import frostfs.object.Service; import info.frostfs.sdk.dto.object.patch.Range; import org.apache.commons.collections4.CollectionUtils; +import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import static java.util.Objects.isNull; public class RangeMapper { - private RangeMapper() { } public static List toGrpcMessages(List ranges) { if (CollectionUtils.isEmpty(ranges)) { - return null; + return Collections.emptyList(); } return ranges.stream().map(RangeMapper::toGrpcMessage).collect(Collectors.toList()); diff --git a/models/src/test/java/info/frostfs/sdk/mappers/container/ContainerMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/container/ContainerMapperTest.java index 4631766..fadde6a 100644 --- a/models/src/test/java/info/frostfs/sdk/mappers/container/ContainerMapperTest.java +++ b/models/src/test/java/info/frostfs/sdk/mappers/container/ContainerMapperTest.java @@ -5,8 +5,11 @@ import frostfs.container.Types; import info.frostfs.sdk.dto.container.Container; import info.frostfs.sdk.dto.netmap.PlacementPolicy; import info.frostfs.sdk.dto.netmap.Replica; +import info.frostfs.sdk.dto.netmap.Version; +import info.frostfs.sdk.dto.object.OwnerId; import info.frostfs.sdk.enums.BasicAcl; import info.frostfs.sdk.exceptions.ProcessFrostFSException; +import info.frostfs.sdk.mappers.object.OwnerIdMapper; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; @@ -19,12 +22,54 @@ import static info.frostfs.sdk.constants.AttributeConst.DISABLE_HOMOMORPHIC_HASH import static org.junit.jupiter.api.Assertions.*; public class ContainerMapperTest { + private static final String OWNER_ID = "NVxUSpEEJzYXZZtUs18PrJTD9QZkLLNQ8S"; + + @Test + void toGrpcMessage_successFullMessage() { + //Given + var placementPolicy = new PlacementPolicy(new Replica[]{new Replica(3)}, true, 1); + var container = new Container(placementPolicy); + container.getAttributes().put("key1", "val1"); + container.getAttributes().put(DISABLE_HOMOMORPHIC_HASHING_ATTRIBUTE, "false"); + container.setVersion(new Version()); + container.setOwnerId(new OwnerId(OWNER_ID)); + + //When + var result = ContainerMapper.toGrpcMessage(container); + + //Then + assertNotNull(result); + assertEquals(container.getNonce(), asUuid(result.getNonce().toByteArray())); + assertEquals(container.getPlacementPolicy().isUnique(), result.getPlacementPolicy().getUnique()); + assertEquals( + container.getPlacementPolicy().getBackupFactory(), + result.getPlacementPolicy().getContainerBackupFactor() + ); + assertEquals(placementPolicy.getReplicas().length, result.getPlacementPolicy().getReplicasCount()); + assertEquals( + container.getPlacementPolicy().getReplicas()[0].getCount(), + result.getPlacementPolicy().getReplicasList().get(0).getCount() + ); + assertEquals( + container.getPlacementPolicy().getReplicas()[0].getSelector(), + result.getPlacementPolicy().getReplicasList().get(0).getSelector() + ); + + assertEquals("key1", result.getAttributes(0).getKey()); + assertEquals("val1", result.getAttributes(0).getValue()); + assertEquals(DISABLE_HOMOMORPHIC_HASHING_ATTRIBUTE, result.getAttributes(1).getKey()); + assertEquals("false", result.getAttributes(1).getValue()); + + assertEquals(container.getVersion().getMajor(), result.getVersion().getMajor()); + assertEquals(container.getVersion().getMinor(), result.getVersion().getMinor()); + assertEquals(container.getOwnerId().toString(), OwnerIdMapper.toModel(result.getOwnerId()).toString()); + } @Test void toGrpcMessage_success() { //Given - var placementPolicy = new PlacementPolicy(new Replica[]{new Replica(1)}, true); - var container = new Container(BasicAcl.PUBLIC_RW, placementPolicy); + var placementPolicy = new PlacementPolicy(new Replica[]{new Replica(3)}, true, 1); + var container = new Container(placementPolicy); container.getAttributes().put("key1", "val1"); container.getAttributes().put(DISABLE_HOMOMORPHIC_HASHING_ATTRIBUTE, "false"); @@ -33,9 +78,12 @@ public class ContainerMapperTest { //Then assertNotNull(result); - assertEquals(container.getBasicAcl().value, result.getBasicAcl()); assertEquals(container.getNonce(), asUuid(result.getNonce().toByteArray())); assertEquals(container.getPlacementPolicy().isUnique(), result.getPlacementPolicy().getUnique()); + assertEquals( + container.getPlacementPolicy().getBackupFactory(), + result.getPlacementPolicy().getContainerBackupFactor() + ); assertEquals(placementPolicy.getReplicas().length, result.getPlacementPolicy().getReplicasCount()); assertEquals( container.getPlacementPolicy().getReplicas()[0].getCount(), @@ -75,6 +123,7 @@ public class ContainerMapperTest { var placementPolicy = frostfs.netmap.Types.PlacementPolicy.newBuilder() .setUnique(true) .addReplicas(replica) + .setContainerBackupFactor(2) .build(); var attribute1 = Types.Container.Attribute.newBuilder() @@ -101,9 +150,12 @@ public class ContainerMapperTest { //Then assertNotNull(result); - assertEquals(container.getBasicAcl(), result.getBasicAcl().value); assertEquals(asUuid(container.getNonce().toByteArray()), result.getNonce()); assertEquals(container.getPlacementPolicy().getUnique(), result.getPlacementPolicy().isUnique()); + assertEquals( + container.getPlacementPolicy().getContainerBackupFactor(), + result.getPlacementPolicy().getBackupFactory() + ); assertEquals(placementPolicy.getReplicasCount(), result.getPlacementPolicy().getReplicas().length); assertEquals( container.getPlacementPolicy().getReplicasList().get(0).getCount(), diff --git a/models/src/test/java/info/frostfs/sdk/mappers/netmap/FilterMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/netmap/FilterMapperTest.java new file mode 100644 index 0000000..4b1649d --- /dev/null +++ b/models/src/test/java/info/frostfs/sdk/mappers/netmap/FilterMapperTest.java @@ -0,0 +1,209 @@ +package info.frostfs.sdk.mappers.netmap; + +import frostfs.netmap.Types; +import info.frostfs.sdk.dto.netmap.Filter; +import info.frostfs.sdk.enums.FilterOperation; +import info.frostfs.sdk.exceptions.ProcessFrostFSException; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.mockito.MockedStatic; + +import java.util.Collections; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mockStatic; + +public class FilterMapperTest { + + @ParameterizedTest + @EnumSource(value = FilterOperation.class) + void toGrpcMessages_success(FilterOperation operation) { + //Given + var filter1 = new Filter("name1", "key1", operation, "value1", null); + var filter2 = new Filter("name2", "key2", operation, "value2", null); + + //When + var result = FilterMapper.toGrpcMessages(new Filter[]{filter1, filter2}); + + //Then + assertThat(result).isNotNull().hasSize(2); + + assertEquals(filter1.getKey(), result.get(0).getKey()); + assertEquals(filter1.getName(), result.get(0).getName()); + assertEquals(filter1.getOperation().value, result.get(0).getOpValue()); + assertEquals(filter1.getValue(), result.get(0).getValue()); + assertEquals(0, result.get(0).getFiltersCount()); + + assertEquals(filter2.getKey(), result.get(1).getKey()); + assertEquals(filter2.getName(), result.get(1).getName()); + assertEquals(filter2.getOperation().value, result.get(1).getOpValue()); + assertEquals(filter2.getValue(), result.get(1).getValue()); + assertEquals(0, result.get(1).getFiltersCount()); + } + + @Test + void toGrpcMessages_null() { + //When + Then + assertEquals(Collections.emptyList(), FilterMapper.toGrpcMessages(null)); + assertEquals(Collections.emptyList(), FilterMapper.toGrpcMessages(new Filter[]{})); + } + + @ParameterizedTest + @EnumSource(value = FilterOperation.class) + void toGrpcMessage_success(FilterOperation operation) { + //Given + var filterChild = new Filter("name1", "key1", operation, "value1", null); + var filterParent = new Filter("name2", "key2", operation, "value2", new Filter[]{filterChild}); + + //When + var result = FilterMapper.toGrpcMessage(filterParent); + + //Then + assertNotNull(result); + assertEquals(filterParent.getKey(), result.getKey()); + assertEquals(filterParent.getName(), result.getName()); + assertEquals(filterParent.getOperation().value, result.getOpValue()); + assertEquals(filterParent.getValue(), result.getValue()); + assertEquals(filterParent.getFilters().length, result.getFiltersCount()); + + var filterChildGrpc = result.getFilters(0); + assertEquals(filterChild.getKey(), filterChildGrpc.getKey()); + assertEquals(filterChild.getName(), filterChildGrpc.getName()); + assertEquals(filterChild.getOperation().value, filterChildGrpc.getOpValue()); + assertEquals(filterChild.getValue(), filterChildGrpc.getValue()); + assertEquals(0, filterChildGrpc.getFiltersCount()); + } + + @Test + void toGrpcMessage_null() { + //When + Then + assertThrows(ValidationFrostFSException.class, () -> FilterMapper.toGrpcMessage(null)); + } + + @Test + void toGrpcMessage_notValidOperation() { + //Given + var filter = new Filter("name1", "key1", FilterOperation.EQ, "value1", null); + + + //When + Then + try (MockedStatic mockStatic = mockStatic(Types.Operation.class)) { + mockStatic.when(() -> Types.Operation.forNumber(filter.getOperation().value)) + .thenReturn(null); + + assertThrows(ProcessFrostFSException.class, () -> FilterMapper.toGrpcMessage(filter)); + } + } + + @ParameterizedTest + @EnumSource(value = Types.Operation.class, names = "UNRECOGNIZED", mode = EnumSource.Mode.EXCLUDE) + void toModels_success(Types.Operation operation) { + //Given + var filter1 = Types.Filter.newBuilder() + .setName("name1") + .setKey("key1") + .setOp(operation) + .setValue("value1") + .build(); + var filter2 = Types.Filter.newBuilder() + .setName("name2") + .setKey("key2") + .setOp(operation) + .setValue("value2") + .build(); + + //When + var result = FilterMapper.toModels(List.of(filter1, filter2)); + + //Then + assertThat(result).isNotNull().hasSize(2); + + + assertNotNull(result); + assertEquals(filter1.getKey(), result[0].getKey()); + assertEquals(filter1.getName(), result[0].getName()); + assertEquals(filter1.getOpValue(), result[0].getOperation().value); + assertEquals(filter1.getValue(), result[0].getValue()); + assertNull(result[0].getFilters()); + + assertEquals(filter2.getKey(), result[1].getKey()); + assertEquals(filter2.getName(), result[1].getName()); + assertEquals(filter2.getOpValue(), result[1].getOperation().value); + assertEquals(filter2.getValue(), result[1].getValue()); + assertNull(result[1].getFilters()); + } + + @Test + void toModels_null() { + //When + Then + assertNull(FilterMapper.toModels(null)); + assertNull(FilterMapper.toModels(Collections.emptyList())); + } + + @ParameterizedTest + @EnumSource(value = Types.Operation.class, names = "UNRECOGNIZED", mode = EnumSource.Mode.EXCLUDE) + void toModel_success(Types.Operation operation) { + //Given + var filterChild = Types.Filter.newBuilder() + .setName("name1") + .setKey("key1") + .setOp(operation) + .setValue("value1") + .build(); + var filterParent = Types.Filter.newBuilder() + .setName("name2") + .setKey("key2") + .setOp(operation) + .setValue("value2") + .addFilters(filterChild) + .build(); + + //When + var result = FilterMapper.toModel(filterParent); + + //Then + assertNotNull(result); + assertEquals(filterParent.getKey(), result.getKey()); + assertEquals(filterParent.getName(), result.getName()); + assertEquals(filterParent.getOpValue(), result.getOperation().value); + assertEquals(filterParent.getValue(), result.getValue()); + assertEquals(filterParent.getFiltersCount(), result.getFilters().length); + + var filterChildModel = result.getFilters()[0]; + assertEquals(filterChild.getKey(), filterChildModel.getKey()); + assertEquals(filterChild.getName(), filterChildModel.getName()); + assertEquals(filterChild.getOpValue(), filterChildModel.getOperation().value); + assertEquals(filterChild.getValue(), filterChildModel.getValue()); + assertNull(filterChildModel.getFilters()); + } + + @Test + void toModel_null() { + //When + Then + assertNull(FilterMapper.toModel(null)); + assertNull(FilterMapper.toModel(Types.Filter.getDefaultInstance())); + } + + @Test + void toModel_notValidScheme() { + //Given + var filter = Types.Filter.newBuilder() + .setName("name1") + .setKey("key1") + .setOp(Types.Operation.EQ) + .setValue("value1") + .build(); + + //When + Then + try (MockedStatic mockStatic = mockStatic(FilterOperation.class)) { + mockStatic.when(() -> FilterOperation.get(Types.Operation.EQ.getNumber())) + .thenReturn(null); + + assertThrows(ProcessFrostFSException.class, () -> FilterMapper.toModel(filter)); + } + } +} diff --git a/models/src/test/java/info/frostfs/sdk/mappers/netmap/PlacementPolicyMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/netmap/PlacementPolicyMapperTest.java index 903d574..793ad92 100644 --- a/models/src/test/java/info/frostfs/sdk/mappers/netmap/PlacementPolicyMapperTest.java +++ b/models/src/test/java/info/frostfs/sdk/mappers/netmap/PlacementPolicyMapperTest.java @@ -15,7 +15,7 @@ public class PlacementPolicyMapperTest { var replica1 = new Replica(1, "test1"); var replica2 = new Replica(2, "test2"); - var placementPolicy = new PlacementPolicy(new Replica[]{replica1, replica2}, true); + var placementPolicy = new PlacementPolicy(new Replica[]{replica1, replica2}, true, 1); //When var result = PlacementPolicyMapper.toGrpcMessage(placementPolicy); @@ -23,6 +23,7 @@ public class PlacementPolicyMapperTest { //Then assertNotNull(result); assertEquals(placementPolicy.isUnique(), result.getUnique()); + assertEquals(placementPolicy.getBackupFactory(), result.getContainerBackupFactor()); assertEquals(placementPolicy.getReplicas().length, result.getReplicasCount()); assertEquals(replica1.getCount(), result.getReplicas(0).getCount()); assertEquals(replica1.getSelector(), result.getReplicas(0).getSelector()); @@ -53,6 +54,7 @@ public class PlacementPolicyMapperTest { .setUnique(true) .addReplicas(replica1) .addReplicas(replica2) + .setContainerBackupFactor(1) .build(); //When @@ -61,6 +63,7 @@ public class PlacementPolicyMapperTest { //Then assertNotNull(result); assertEquals(placementPolicy.getUnique(), result.isUnique()); + assertEquals(placementPolicy.getContainerBackupFactor(), result.getBackupFactory()); assertEquals(placementPolicy.getReplicasCount(), result.getReplicas().length); assertEquals(replica1.getCount(), result.getReplicas()[0].getCount()); assertEquals(replica1.getSelector(), result.getReplicas()[0].getSelector()); diff --git a/models/src/test/java/info/frostfs/sdk/mappers/netmap/ReplicaMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/netmap/ReplicaMapperTest.java index 973e74c..ca486dc 100644 --- a/models/src/test/java/info/frostfs/sdk/mappers/netmap/ReplicaMapperTest.java +++ b/models/src/test/java/info/frostfs/sdk/mappers/netmap/ReplicaMapperTest.java @@ -2,6 +2,7 @@ package info.frostfs.sdk.mappers.netmap; import frostfs.netmap.Types; import info.frostfs.sdk.dto.netmap.Replica; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; @@ -25,7 +26,7 @@ public class ReplicaMapperTest { @Test void toGrpcMessage_null() { //When + Then - assertNull(ReplicaMapper.toGrpcMessage(null)); + assertThrows(ValidationFrostFSException.class, () -> ReplicaMapper.toGrpcMessage(null)); } @Test diff --git a/models/src/test/java/info/frostfs/sdk/mappers/netmap/SelectorMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/netmap/SelectorMapperTest.java new file mode 100644 index 0000000..4e55ae0 --- /dev/null +++ b/models/src/test/java/info/frostfs/sdk/mappers/netmap/SelectorMapperTest.java @@ -0,0 +1,191 @@ +package info.frostfs.sdk.mappers.netmap; + +import frostfs.netmap.Types; +import info.frostfs.sdk.dto.netmap.Selector; +import info.frostfs.sdk.enums.SelectorClause; +import info.frostfs.sdk.exceptions.ProcessFrostFSException; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.mockito.MockedStatic; + +import java.util.Collections; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mockStatic; + +public class SelectorMapperTest { + + @ParameterizedTest + @EnumSource(value = SelectorClause.class) + void toGrpcMessages_success(SelectorClause clause) { + //Given + var selector1 = new Selector("name1", 1, clause, "attribute1", "filter1"); + var selector2 = new Selector("name2", 2, clause, "attribute2", "filter2"); + + //When + var result = SelectorMapper.toGrpcMessages(new Selector[]{selector1, selector2}); + + //Then + assertThat(result).isNotNull().hasSize(2); + + assertEquals(selector1.getName(), result.get(0).getName()); + assertEquals(selector1.getCount(), result.get(0).getCount()); + assertEquals(selector1.getClause().value, result.get(0).getClauseValue()); + assertEquals(selector1.getAttribute(), result.get(0).getAttribute()); + assertEquals(selector1.getFilter(), result.get(0).getFilter()); + + assertEquals(selector2.getName(), result.get(1).getName()); + assertEquals(selector2.getCount(), result.get(1).getCount()); + assertEquals(selector2.getClause().value, result.get(1).getClauseValue()); + assertEquals(selector2.getAttribute(), result.get(1).getAttribute()); + assertEquals(selector2.getFilter(), result.get(1).getFilter()); + } + + @Test + void toGrpcMessages_null() { + //When + Then + assertEquals(Collections.emptyList(), SelectorMapper.toGrpcMessages(null)); + assertEquals(Collections.emptyList(), SelectorMapper.toGrpcMessages(new Selector[]{})); + } + + @ParameterizedTest + @EnumSource(value = SelectorClause.class) + void toGrpcMessage_success(SelectorClause clause) { + //Given + var selector = new Selector("name", 1, clause, "attribute", "filter"); + + //When + var result = SelectorMapper.toGrpcMessage(selector); + + //Then + assertNotNull(result); + assertEquals(selector.getName(), result.getName()); + assertEquals(selector.getCount(), result.getCount()); + assertEquals(selector.getClause().value, result.getClauseValue()); + assertEquals(selector.getAttribute(), result.getAttribute()); + assertEquals(selector.getFilter(), result.getFilter()); + } + + @Test + void toGrpcMessage_null() { + //When + Then + assertThrows(ValidationFrostFSException.class, () -> SelectorMapper.toGrpcMessage(null)); + } + + @Test + void toGrpcMessage_notValidOperation() { + //Given + var selector = new Selector("name", 1, SelectorClause.SAME, "attribute", "filter"); + + + //When + Then + try (MockedStatic mockStatic = mockStatic(Types.Clause.class)) { + mockStatic.when(() -> Types.Clause.forNumber(selector.getClause().value)) + .thenReturn(null); + + assertThrows(ProcessFrostFSException.class, () -> SelectorMapper.toGrpcMessage(selector)); + } + } + + @ParameterizedTest + @EnumSource(value = Types.Clause.class, names = "UNRECOGNIZED", mode = EnumSource.Mode.EXCLUDE) + void toModels_success(Types.Clause clause) { + //Given + var selector1 = Types.Selector.newBuilder() + .setName("name1") + .setCount(1) + .setClause(clause) + .setAttribute("attribute1") + .setFilter("filter1") + .build(); + var selector2 = Types.Selector.newBuilder() + .setName("name2") + .setCount(2) + .setClause(clause) + .setAttribute("attribute2") + .setFilter("filter2") + .build(); + + //When + var result = SelectorMapper.toModels(List.of(selector1, selector2)); + + //Then + assertThat(result).isNotNull().hasSize(2); + + + assertNotNull(result); + assertEquals(selector1.getName(), result[0].getName()); + assertEquals(selector1.getCount(), result[0].getCount()); + assertEquals(selector1.getClauseValue(), result[0].getClause().value); + assertEquals(selector1.getAttribute(), result[0].getAttribute()); + assertEquals(selector1.getFilter(), result[0].getFilter()); + + assertEquals(selector2.getName(), result[1].getName()); + assertEquals(selector2.getCount(), result[1].getCount()); + assertEquals(selector2.getClauseValue(), result[1].getClause().value); + assertEquals(selector2.getAttribute(), result[1].getAttribute()); + assertEquals(selector2.getFilter(), result[1].getFilter()); + } + + @Test + void toModels_null() { + //When + Then + assertNull(SelectorMapper.toModels(null)); + assertNull(SelectorMapper.toModels(Collections.emptyList())); + } + + @ParameterizedTest + @EnumSource(value = Types.Clause.class, names = "UNRECOGNIZED", mode = EnumSource.Mode.EXCLUDE) + void toModel_success(Types.Clause clause) { + //Given + var selector = Types.Selector.newBuilder() + .setName("name") + .setCount(1) + .setClause(clause) + .setAttribute("attribute") + .setFilter("filter") + .build(); + + //When + var result = SelectorMapper.toModel(selector); + + //Then + assertNotNull(result); + assertEquals(selector.getName(), result.getName()); + assertEquals(selector.getCount(), result.getCount()); + assertEquals(selector.getClauseValue(), result.getClause().value); + assertEquals(selector.getAttribute(), result.getAttribute()); + assertEquals(selector.getFilter(), result.getFilter()); + } + + @Test + void toModel_null() { + //When + Then + assertNull(SelectorMapper.toModel(null)); + assertNull(SelectorMapper.toModel(Types.Selector.getDefaultInstance())); + } + + @Test + void toModel_notValidScheme() { + //Given + var selector = Types.Selector.newBuilder() + .setName("name") + .setCount(1) + .setClause(Types.Clause.SAME) + .setAttribute("attribute") + .setFilter("filter") + .build(); + + //When + Then + try (MockedStatic mockStatic = mockStatic(SelectorClause.class)) { + mockStatic.when(() -> SelectorClause.get(Types.Clause.SAME.getNumber())) + .thenReturn(null); + + assertThrows(ProcessFrostFSException.class, () -> SelectorMapper.toModel(selector)); + } + } +} diff --git a/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectAttributeMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectAttributeMapperTest.java index 160af7a..4eb5501 100644 --- a/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectAttributeMapperTest.java +++ b/models/src/test/java/info/frostfs/sdk/mappers/object/ObjectAttributeMapperTest.java @@ -2,6 +2,7 @@ package info.frostfs.sdk.mappers.object; import frostfs.object.Types; import info.frostfs.sdk.dto.object.ObjectAttribute; +import org.apache.commons.collections4.CollectionUtils; import org.junit.jupiter.api.Test; import java.util.Arrays; @@ -33,8 +34,8 @@ public class ObjectAttributeMapperTest { @Test void toGrpcMessages_null() { //When + Then - assertNull(ObjectAttributeMapper.toGrpcMessages(null)); - assertNull(ObjectAttributeMapper.toGrpcMessages(Collections.emptyList())); + assertTrue(CollectionUtils.isEmpty(ObjectAttributeMapper.toGrpcMessages(null))); + assertTrue(CollectionUtils.isEmpty(ObjectAttributeMapper.toGrpcMessages(Collections.emptyList()))); } @Test diff --git a/models/src/test/java/info/frostfs/sdk/mappers/object/patch/RangeMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/object/patch/RangeMapperTest.java index 771a9ac..f78a482 100644 --- a/models/src/test/java/info/frostfs/sdk/mappers/object/patch/RangeMapperTest.java +++ b/models/src/test/java/info/frostfs/sdk/mappers/object/patch/RangeMapperTest.java @@ -1,6 +1,7 @@ package info.frostfs.sdk.mappers.object.patch; import info.frostfs.sdk.dto.object.patch.Range; +import org.apache.commons.collections4.CollectionUtils; import org.junit.jupiter.api.Test; import java.util.Arrays; @@ -32,8 +33,8 @@ public class RangeMapperTest { @Test void toGrpcMessages_null() { //When + Then - assertNull(RangeMapper.toGrpcMessages(null)); - assertNull(RangeMapper.toGrpcMessages(Collections.emptyList())); + assertTrue(CollectionUtils.isEmpty(RangeMapper.toGrpcMessages(null))); + assertTrue(CollectionUtils.isEmpty(RangeMapper.toGrpcMessages(Collections.emptyList()))); } @Test diff --git a/pom.xml b/pom.xml index 2f42332..8f58e1f 100644 --- a/pom.xml +++ b/pom.xml @@ -17,7 +17,7 @@ - 0.6.0 + 0.7.0 11 11 From db749194013ff38ae8507e24a6810b5d79a8e868 Mon Sep 17 00:00:00 2001 From: Ori Bruk Date: Tue, 4 Mar 2025 17:01:51 +0300 Subject: [PATCH 33/38] [#45] Add the ability to create a client via wallet and password Signed-off-by: Ori Bruk --- CHANGELOG.md | 5 + README.md | 3 +- .../java/info/frostfs/sdk/FrostFSClient.java | 8 +- .../ComplexAtLeastOneIsFilled.java | 12 ++ .../frostfs/sdk/jdo/ClientEnvironment.java | 16 +- .../info/frostfs/sdk/jdo/ClientSettings.java | 46 ++++-- .../main/java/info/frostfs/sdk/jdo/ECDsa.java | 45 +++++- .../info/frostfs/sdk/utils/Validator.java | 24 ++- .../sdk/services/AccountingClientTest.java | 2 +- .../main/java/info/frostfs/sdk/Base58.java | 141 ------------------ .../main/java/info/frostfs/sdk/Helper.java | 14 -- .../java/info/frostfs/sdk/KeyExtension.java | 105 +------------ .../java/info/frostfs/sdk/Base58Test.java | 56 ------- .../java/info/frostfs/sdk/HelperTest.java | 32 ---- .../info/frostfs/sdk/KeyExtensionTest.java | 91 ----------- .../frostfs/sdk/constants/ErrorConst.java | 8 +- .../sdk/dto/container/ContainerId.java | 2 +- .../info/frostfs/sdk/dto/object/ObjectId.java | 2 +- .../info/frostfs/sdk/dto/object/OwnerId.java | 13 +- .../java/info/frostfs/sdk/enums/BasicAcl.java | 33 ---- .../mappers/container/ContainerMapper.java | 10 -- .../sdk/mappers/object/OwnerIdMapper.java | 2 +- .../container/ContainerMapperTest.java | 21 +-- pom.xml | 8 +- 24 files changed, 141 insertions(+), 558 deletions(-) create mode 100644 client/src/main/java/info/frostfs/sdk/annotations/ComplexAtLeastOneIsFilled.java delete mode 100644 cryptography/src/main/java/info/frostfs/sdk/Base58.java delete mode 100644 cryptography/src/test/java/info/frostfs/sdk/Base58Test.java delete mode 100644 models/src/main/java/info/frostfs/sdk/enums/BasicAcl.java diff --git a/CHANGELOG.md b/CHANGELOG.md index d9e6a86..38ab6d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## [0.8.0] - 2025-03-04 + +### Added +- Creating client via wallet and password + ## [0.7.0] - 2025-02-20 ### Added diff --git a/README.md b/README.md index fd1efff..991c5fa 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,6 @@ import info.frostfs.sdk.FrostFSClient; import info.frostfs.sdk.dto.container.Container; import info.frostfs.sdk.dto.netmap.PlacementPolicy; import info.frostfs.sdk.dto.netmap.Replica; -import info.frostfs.sdk.enums.BasicAcl; import info.frostfs.sdk.jdo.ClientSettings; import info.frostfs.sdk.jdo.parameters.CallContext; import info.frostfs.sdk.jdo.parameters.container.PrmContainerCreate; @@ -41,7 +40,7 @@ public class ContainerExample { FrostFSClient frostFSClient = new FrostFSClient(clientSettings); // Create container - var placementPolicy = new PlacementPolicy(new Replica[]{new Replica(1)}, true, 0); + var placementPolicy = new PlacementPolicy(new Replica[]{new Replica(3)}, true, 1); var prmContainerCreate = new PrmContainerCreate(new Container(placementPolicy)); var containerId = frostFSClient.createContainer(prmContainerCreate, callContext); diff --git a/client/src/main/java/info/frostfs/sdk/FrostFSClient.java b/client/src/main/java/info/frostfs/sdk/FrostFSClient.java index 0026285..c81c218 100644 --- a/client/src/main/java/info/frostfs/sdk/FrostFSClient.java +++ b/client/src/main/java/info/frostfs/sdk/FrostFSClient.java @@ -13,6 +13,7 @@ import info.frostfs.sdk.dto.session.SessionToken; import info.frostfs.sdk.exceptions.ProcessFrostFSException; import info.frostfs.sdk.jdo.ClientEnvironment; import info.frostfs.sdk.jdo.ClientSettings; +import info.frostfs.sdk.jdo.ECDsa; import info.frostfs.sdk.jdo.NetworkSettings; import info.frostfs.sdk.jdo.parameters.CallContext; import info.frostfs.sdk.jdo.parameters.ape.PrmApeChainAdd; @@ -40,6 +41,7 @@ import info.frostfs.sdk.utils.Validator; import io.grpc.Channel; import io.grpc.ClientInterceptors; import io.grpc.ManagedChannel; +import org.apache.commons.lang3.StringUtils; import java.util.List; @@ -66,10 +68,12 @@ public class FrostFSClient implements CommonClient { ? clientSettings.getChannel() : initGrpcChannel(clientSettings); + var ecdsa = StringUtils.isBlank(clientSettings.getWif()) + ? new ECDsa(clientSettings.getWallet(), clientSettings.getPassword()) + : new ECDsa(clientSettings.getWif()); Channel interceptChannel = ClientInterceptors.intercept(channel, MONITORING_CLIENT_INTERCEPTOR); ClientEnvironment clientEnvironment = new ClientEnvironment( - clientSettings.getKey(), interceptChannel, new Version(), this, - new SessionCache(0) + ecdsa, interceptChannel, new Version(), this, new SessionCache(0) ); Validator.validate(clientEnvironment); diff --git a/client/src/main/java/info/frostfs/sdk/annotations/ComplexAtLeastOneIsFilled.java b/client/src/main/java/info/frostfs/sdk/annotations/ComplexAtLeastOneIsFilled.java new file mode 100644 index 0000000..eb3fe48 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/annotations/ComplexAtLeastOneIsFilled.java @@ -0,0 +1,12 @@ +package info.frostfs.sdk.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE}) +public @interface ComplexAtLeastOneIsFilled { + AtLeastOneIsFilled[] value(); +} diff --git a/client/src/main/java/info/frostfs/sdk/jdo/ClientEnvironment.java b/client/src/main/java/info/frostfs/sdk/jdo/ClientEnvironment.java index 62e0967..387882e 100644 --- a/client/src/main/java/info/frostfs/sdk/jdo/ClientEnvironment.java +++ b/client/src/main/java/info/frostfs/sdk/jdo/ClientEnvironment.java @@ -36,10 +36,10 @@ public class ClientEnvironment { private SessionCache sessionCache; - public ClientEnvironment(String wif, Channel channel, Version version, FrostFSClient frostFSClient, + public ClientEnvironment(ECDsa key, Channel channel, Version version, FrostFSClient frostFSClient, SessionCache sessionCache) { - this.key = new ECDsa(wif); - this.ownerId = new OwnerId(key.getPublicKeyByte()); + this.key = key; + this.ownerId = new OwnerId(key.getAccount().getAddress()); this.version = version; this.channel = channel; this.frostFSClient = frostFSClient; @@ -47,16 +47,6 @@ public class ClientEnvironment { this.address = channel.authority(); } - public ClientEnvironment(ECDsa key, Channel channel, Version version, FrostFSClient frostFSClient, - SessionCache sessionCache) { - this.key = key; - this.ownerId = new OwnerId(key.getPublicKeyByte()); - this.version = version; - this.channel = channel; - this.frostFSClient = frostFSClient; - this.sessionCache = sessionCache; - } - public String getSessionKey() { if (StringUtils.isBlank(sessionKey)) { this.sessionKey = formCacheKey(address, getHexString(key.getPublicKeyByte())); diff --git a/client/src/main/java/info/frostfs/sdk/jdo/ClientSettings.java b/client/src/main/java/info/frostfs/sdk/jdo/ClientSettings.java index 3f8d2df..0bab648 100644 --- a/client/src/main/java/info/frostfs/sdk/jdo/ClientSettings.java +++ b/client/src/main/java/info/frostfs/sdk/jdo/ClientSettings.java @@ -1,37 +1,61 @@ package info.frostfs.sdk.jdo; import info.frostfs.sdk.annotations.AtLeastOneIsFilled; -import info.frostfs.sdk.annotations.NotNull; +import info.frostfs.sdk.annotations.ComplexAtLeastOneIsFilled; import io.grpc.ChannelCredentials; import io.grpc.ManagedChannel; import lombok.Getter; import lombok.experimental.FieldNameConstants; +import java.io.File; + @Getter @FieldNameConstants -@AtLeastOneIsFilled(fields = {ClientSettings.Fields.host, ClientSettings.Fields.channel}) +@ComplexAtLeastOneIsFilled(value = { + @AtLeastOneIsFilled(fields = {ClientSettings.Fields.host, ClientSettings.Fields.channel}), + @AtLeastOneIsFilled(fields = {ClientSettings.Fields.wif, ClientSettings.Fields.wallet}), +}) public class ClientSettings { - @NotNull - private final String key; - + private String wif; + private File wallet; + private String password; private String host; private ChannelCredentials credentials; private ManagedChannel channel; - public ClientSettings(String key, String host) { - this.key = key; + public ClientSettings(String wif, String host) { + this.wif = wif; this.host = host; } - public ClientSettings(String key, String host, ChannelCredentials credentials) { - this.key = key; + public ClientSettings(String wif, String host, ChannelCredentials credentials) { + this.wif = wif; this.host = host; this.credentials = credentials; } - public ClientSettings(String key, ManagedChannel channel) { - this.key = key; + public ClientSettings(String wif, ManagedChannel channel) { + this.wif = wif; + this.channel = channel; + } + + public ClientSettings(File wallet, String password, String host) { + this.wallet = wallet; + this.password = password; + this.host = host; + } + + public ClientSettings(File wallet, String password, String host, ChannelCredentials credentials) { + this.wallet = wallet; + this.password = password; + this.host = host; + this.credentials = credentials; + } + + public ClientSettings(File wallet, String password, ManagedChannel channel) { + this.wallet = wallet; + this.password = password; this.channel = channel; } } diff --git a/client/src/main/java/info/frostfs/sdk/jdo/ECDsa.java b/client/src/main/java/info/frostfs/sdk/jdo/ECDsa.java index b7e4686..dcd5b0a 100644 --- a/client/src/main/java/info/frostfs/sdk/jdo/ECDsa.java +++ b/client/src/main/java/info/frostfs/sdk/jdo/ECDsa.java @@ -1,14 +1,25 @@ package info.frostfs.sdk.jdo; import info.frostfs.sdk.annotations.NotNull; +import info.frostfs.sdk.exceptions.FrostFSException; import info.frostfs.sdk.exceptions.ValidationFrostFSException; +import io.neow3j.wallet.Account; +import io.neow3j.wallet.nep6.NEP6Account; +import io.neow3j.wallet.nep6.NEP6Wallet; import lombok.Getter; import org.apache.commons.lang3.StringUtils; +import java.io.File; +import java.io.FileInputStream; import java.security.PrivateKey; +import java.util.Optional; -import static info.frostfs.sdk.KeyExtension.*; +import static info.frostfs.sdk.KeyExtension.loadPrivateKey; +import static info.frostfs.sdk.constants.ErrorConst.WALLET_IS_INVALID; import static info.frostfs.sdk.constants.ErrorConst.WIF_IS_INVALID; +import static info.frostfs.sdk.constants.FieldConst.EMPTY_STRING; +import static io.neow3j.wallet.Wallet.OBJECT_MAPPER; +import static java.util.Objects.isNull; @Getter public class ECDsa { @@ -22,13 +33,41 @@ public class ECDsa { @NotNull private final PrivateKey privateKey; + @NotNull + private final Account account; + public ECDsa(String wif) { if (StringUtils.isEmpty(wif)) { throw new ValidationFrostFSException(WIF_IS_INVALID); } - this.privateKeyByte = getPrivateKeyFromWIF(wif); - this.publicKeyByte = loadPublicKey(privateKeyByte); + this.account = Account.fromWIF(wif); + this.privateKeyByte = account.getECKeyPair().getPrivateKey().getBytes(); + this.publicKeyByte = account.getECKeyPair().getPublicKey().getEncoded(true); this.privateKey = loadPrivateKey(privateKeyByte); } + + public ECDsa(File walletFile, String password) { + if (isNull(walletFile)) { + throw new ValidationFrostFSException(WALLET_IS_INVALID); + } + + try (var walletStream = new FileInputStream(walletFile)) { + NEP6Wallet nep6Wallet = OBJECT_MAPPER.readValue(walletStream, NEP6Wallet.class); + Optional defaultAccount = nep6Wallet.getAccounts().stream() + .filter(NEP6Account::getDefault) + .findFirst(); + + var account = defaultAccount.map(Account::fromNEP6Account) + .orElseGet(() -> Account.fromNEP6Account(nep6Wallet.getAccounts().get(0))); + account.decryptPrivateKey(isNull(password) ? EMPTY_STRING : password); + + this.account = account; + this.privateKeyByte = account.getECKeyPair().getPrivateKey().getBytes(); + this.publicKeyByte = account.getECKeyPair().getPublicKey().getEncoded(true); + this.privateKey = loadPrivateKey(privateKeyByte); + } catch (Exception exp) { + throw new FrostFSException(exp.getMessage()); + } + } } diff --git a/client/src/main/java/info/frostfs/sdk/utils/Validator.java b/client/src/main/java/info/frostfs/sdk/utils/Validator.java index 316f4c8..5d61493 100644 --- a/client/src/main/java/info/frostfs/sdk/utils/Validator.java +++ b/client/src/main/java/info/frostfs/sdk/utils/Validator.java @@ -1,9 +1,6 @@ package info.frostfs.sdk.utils; -import info.frostfs.sdk.annotations.AtLeastOneIsFilled; -import info.frostfs.sdk.annotations.NotBlank; -import info.frostfs.sdk.annotations.NotNull; -import info.frostfs.sdk.annotations.Validate; +import info.frostfs.sdk.annotations.*; import info.frostfs.sdk.constants.ErrorConst; import info.frostfs.sdk.exceptions.ProcessFrostFSException; import info.frostfs.sdk.exceptions.ValidationFrostFSException; @@ -37,6 +34,10 @@ public class Validator { Class clazz = object.getClass(); + if (clazz.isAnnotationPresent(ComplexAtLeastOneIsFilled.class)) { + processComplexAtLeastOneIsFilled(object, clazz, errorMessage); + } + if (clazz.isAnnotationPresent(AtLeastOneIsFilled.class)) { processAtLeastOneIsFilled(object, clazz, errorMessage); } @@ -83,8 +84,22 @@ public class Validator { process(getFieldValue(object, field), errorMessage); } + private static void processComplexAtLeastOneIsFilled(T object, Class clazz, StringBuilder errorMessage) { + var annotation = clazz.getAnnotation(ComplexAtLeastOneIsFilled.class); + for (AtLeastOneIsFilled value : annotation.value()) { + processAtLeastOneIsFilled(object, clazz, errorMessage, value); + } + } + private static void processAtLeastOneIsFilled(T object, Class clazz, StringBuilder errorMessage) { var annotation = clazz.getAnnotation(AtLeastOneIsFilled.class); + processAtLeastOneIsFilled(object, clazz, errorMessage, annotation); + } + + private static void processAtLeastOneIsFilled(T object, + Class clazz, + StringBuilder errorMessage, + AtLeastOneIsFilled annotation) { var emptyFieldsCount = 0; for (String fieldName : annotation.fields()) { var field = getField(clazz, fieldName); @@ -106,6 +121,7 @@ public class Validator { } } + private static Object getFieldValue(T object, Field field) { try { return field.get(object); diff --git a/client/src/test/java/info/frostfs/sdk/services/AccountingClientTest.java b/client/src/test/java/info/frostfs/sdk/services/AccountingClientTest.java index b137af2..741475c 100644 --- a/client/src/test/java/info/frostfs/sdk/services/AccountingClientTest.java +++ b/client/src/test/java/info/frostfs/sdk/services/AccountingClientTest.java @@ -2,7 +2,6 @@ package info.frostfs.sdk.services; import frostfs.accounting.AccountingServiceGrpc; import frostfs.accounting.Service; -import info.frostfs.sdk.Base58; import info.frostfs.sdk.dto.object.OwnerId; import info.frostfs.sdk.jdo.ClientEnvironment; import info.frostfs.sdk.jdo.parameters.CallContext; @@ -12,6 +11,7 @@ import info.frostfs.sdk.tools.RequestConstructor; import info.frostfs.sdk.tools.RequestSigner; import info.frostfs.sdk.tools.Verifier; import io.grpc.Channel; +import io.neow3j.crypto.Base58; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/cryptography/src/main/java/info/frostfs/sdk/Base58.java b/cryptography/src/main/java/info/frostfs/sdk/Base58.java deleted file mode 100644 index d302763..0000000 --- a/cryptography/src/main/java/info/frostfs/sdk/Base58.java +++ /dev/null @@ -1,141 +0,0 @@ -package info.frostfs.sdk; - -import info.frostfs.sdk.exceptions.ProcessFrostFSException; -import info.frostfs.sdk.exceptions.ValidationFrostFSException; -import org.apache.commons.lang3.StringUtils; - -import java.util.Arrays; - -import static info.frostfs.sdk.ArrayHelper.concat; -import static info.frostfs.sdk.Helper.getSha256; -import static info.frostfs.sdk.constants.ErrorConst.*; -import static java.util.Objects.isNull; - -public class Base58 { - public static final char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray(); - public static final int BASE58_SYMBOL_COUNT = 58; - public static final int BASE256_SYMBOL_COUNT = 256; - private static final int BYTE_DIVISION = 0xFF; - private static final char ENCODED_ZERO = ALPHABET[0]; - private static final char BASE58_ASCII_MAX_VALUE = 128; - private static final int[] INDEXES = new int[BASE58_ASCII_MAX_VALUE]; - - static { - Arrays.fill(INDEXES, -1); - for (int i = 0; i < ALPHABET.length; i++) { - INDEXES[ALPHABET[i]] = i; - } - } - - private Base58() { - } - - public static byte[] base58CheckDecode(String input) { - if (StringUtils.isEmpty(input)) { - throw new ValidationFrostFSException(INPUT_PARAM_IS_MISSING); - } - - byte[] buffer = decode(input); - if (buffer.length < 4) { - throw new ProcessFrostFSException(String.format(DECODE_LENGTH_VALUE_TEMPLATE, buffer.length)); - } - - byte[] decode = Arrays.copyOfRange(buffer, 0, buffer.length - 4); - byte[] checksum = getSha256(getSha256(decode)); - var bufferEnd = Arrays.copyOfRange(buffer, buffer.length - 4, buffer.length); - var checksumStart = Arrays.copyOfRange(checksum, 0, 4); - if (!Arrays.equals(bufferEnd, checksumStart)) { - throw new ProcessFrostFSException(INVALID_CHECKSUM); - } - - return decode; - } - - public static String base58CheckEncode(byte[] data) { - if (isNull(data)) { - throw new ValidationFrostFSException(INPUT_PARAM_IS_MISSING); - } - - byte[] checksum = getSha256(getSha256(data)); - var buffer = concat(data, Arrays.copyOfRange(checksum, 0, 4)); - return encode(buffer); - } - - public static String encode(byte[] input) { - if (input.length == 0) { - return ""; - } - // Count leading zeros. - int zeros = 0; - while (zeros < input.length && input[zeros] == 0) { - ++zeros; - } - // Convert base-256 digits to base-58 digits (plus conversion to ASCII characters) - input = Arrays.copyOf(input, input.length); // since we modify it in-place - char[] encoded = new char[input.length * 2]; // upper bound - int outputStart = encoded.length; - for (int inputStart = zeros; inputStart < input.length; ) { - encoded[--outputStart] = ALPHABET[divmod(input, inputStart, BASE256_SYMBOL_COUNT, BASE58_SYMBOL_COUNT)]; - if (input[inputStart] == 0) { - ++inputStart; // optimization - skip leading zeros - } - } - // Preserve exactly as many leading encoded zeros in output as there were leading zeros in input. - while (outputStart < encoded.length && encoded[outputStart] == ENCODED_ZERO) { - ++outputStart; - } - while (--zeros >= 0) { - encoded[--outputStart] = ENCODED_ZERO; - } - // Return encoded string (including encoded leading zeros). - return new String(encoded, outputStart, encoded.length - outputStart); - } - - public static byte[] decode(String input) { - if (input.isEmpty()) { - return new byte[0]; - } - // Convert the base58-encoded ASCII chars to a base58 byte sequence (base58 digits). - byte[] input58 = new byte[input.length()]; - for (int i = 0; i < input.length(); ++i) { - char c = input.charAt(i); - int digit = c < BASE58_ASCII_MAX_VALUE ? INDEXES[c] : -1; - if (digit < 0) { - throw new ValidationFrostFSException(String.format(INVALID_BASE58_CHARACTER_TEMPLATE, (int) c)); - } - input58[i] = (byte) digit; - } - // Count leading zeros. - int zeros = 0; - while (zeros < input58.length && input58[zeros] == 0) { - ++zeros; - } - // Convert base-58 digits to base-256 digits. - byte[] decoded = new byte[input.length()]; - int outputStart = decoded.length; - for (int inputStart = zeros; inputStart < input58.length; ) { - decoded[--outputStart] = divmod(input58, inputStart, BASE58_SYMBOL_COUNT, BASE256_SYMBOL_COUNT); - if (input58[inputStart] == 0) { - ++inputStart; // optimization - skip leading zeros - } - } - // Ignore extra leading zeroes that were added during the calculation. - while (outputStart < decoded.length && decoded[outputStart] == 0) { - ++outputStart; - } - // Return decoded data (including original number of leading zeros). - return Arrays.copyOfRange(decoded, outputStart - zeros, decoded.length); - } - - private static byte divmod(byte[] number, int firstDigit, int base, int divisor) { - // this is just long division which accounts for the base of the input digits - int remainder = 0; - for (int i = firstDigit; i < number.length; i++) { - int digit = (int) number[i] & BYTE_DIVISION; - int temp = remainder * base + digit; - number[i] = (byte) (temp / divisor); - remainder = temp % divisor; - } - return (byte) remainder; - } -} diff --git a/cryptography/src/main/java/info/frostfs/sdk/Helper.java b/cryptography/src/main/java/info/frostfs/sdk/Helper.java index 7ee18d2..0ca1dab 100644 --- a/cryptography/src/main/java/info/frostfs/sdk/Helper.java +++ b/cryptography/src/main/java/info/frostfs/sdk/Helper.java @@ -4,7 +4,6 @@ import com.google.protobuf.ByteString; import com.google.protobuf.Message; import info.frostfs.sdk.exceptions.ValidationFrostFSException; import org.apache.commons.lang3.StringUtils; -import org.bouncycastle.crypto.digests.RIPEMD160Digest; import java.math.BigInteger; import java.security.MessageDigest; @@ -15,24 +14,11 @@ import static java.util.Objects.isNull; public class Helper { private static final String SHA256 = "SHA-256"; - private static final int RIPEMD_160_HASH_BYTE_LENGTH = 20; private static final int HEX_RADIX = 16; private Helper() { } - public static byte[] getRipemd160(byte[] value) { - if (isNull(value)) { - throw new ValidationFrostFSException(INPUT_PARAM_IS_MISSING); - } - - var hash = new byte[RIPEMD_160_HASH_BYTE_LENGTH]; - var digest = new RIPEMD160Digest(); - digest.update(value, 0, value.length); - digest.doFinal(hash, 0); - return hash; - } - public static MessageDigest getSha256Instance() { try { return MessageDigest.getInstance(SHA256); diff --git a/cryptography/src/main/java/info/frostfs/sdk/KeyExtension.java b/cryptography/src/main/java/info/frostfs/sdk/KeyExtension.java index f939c08..248b717 100644 --- a/cryptography/src/main/java/info/frostfs/sdk/KeyExtension.java +++ b/cryptography/src/main/java/info/frostfs/sdk/KeyExtension.java @@ -2,7 +2,6 @@ package info.frostfs.sdk; import info.frostfs.sdk.exceptions.ProcessFrostFSException; import info.frostfs.sdk.exceptions.ValidationFrostFSException; -import org.apache.commons.lang3.StringUtils; import org.bouncycastle.asn1.sec.SECNamedCurves; import org.bouncycastle.asn1.sec.SECObjectIdentifiers; import org.bouncycastle.asn1.x9.X9ECParameters; @@ -10,12 +9,7 @@ import org.bouncycastle.crypto.params.ECDomainParameters; import org.bouncycastle.crypto.params.ECPrivateKeyParameters; import org.bouncycastle.crypto.params.ECPublicKeyParameters; import org.bouncycastle.jce.spec.ECNamedCurveSpec; -import org.bouncycastle.math.ec.ECPoint; -import java.math.BigInteger; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.charset.StandardCharsets; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; @@ -24,63 +18,20 @@ import java.security.spec.ECParameterSpec; import java.security.spec.ECPrivateKeySpec; import java.security.spec.ECPublicKeySpec; import java.security.spec.InvalidKeySpecException; -import java.util.Arrays; -import static info.frostfs.sdk.Helper.getRipemd160; -import static info.frostfs.sdk.Helper.getSha256; -import static info.frostfs.sdk.constants.ErrorConst.*; +import static info.frostfs.sdk.constants.ErrorConst.COMPRESSED_PUBLIC_KEY_WRONG_LENGTH_TEMPLATE; +import static info.frostfs.sdk.constants.ErrorConst.INPUT_PARAM_IS_MISSING; import static java.util.Objects.isNull; import static org.bouncycastle.util.BigIntegers.fromUnsignedByteArray; public class KeyExtension { - public static final byte NEO_ADDRESS_VERSION = 0x35; private static final String CURVE_NAME = "secp256r1"; private static final String SECURITY_ALGORITHM = "EC"; - private static final int PS_IN_HASH160 = 0x0C; - private static final int DECODE_ADDRESS_LENGTH = 21; private static final int COMPRESSED_PUBLIC_KEY_LENGTH = 33; - private static final int UNCOMPRESSED_PUBLIC_KEY_LENGTH = 65; - private static final int CHECK_SIG_DESCRIPTOR = ByteBuffer - .wrap(getSha256("System.Crypto.CheckSig".getBytes(StandardCharsets.US_ASCII))) - .order(ByteOrder.LITTLE_ENDIAN).getInt(); private KeyExtension() { } - public static byte[] compress(byte[] publicKey) { - checkInputValue(publicKey); - if (publicKey.length != UNCOMPRESSED_PUBLIC_KEY_LENGTH) { - throw new ValidationFrostFSException(String.format( - UNCOMPRESSED_PUBLIC_KEY_WRONG_LENGTH_TEMPLATE, UNCOMPRESSED_PUBLIC_KEY_LENGTH, publicKey.length - )); - } - - var secp256R1 = SECNamedCurves.getByOID(SECObjectIdentifiers.secp256r1); - var point = secp256R1.getCurve().decodePoint(publicKey); - return point.getEncoded(true); - } - - public static byte[] getPrivateKeyFromWIF(String wif) { - if (StringUtils.isEmpty(wif)) { - throw new ValidationFrostFSException(INPUT_PARAM_IS_MISSING); - } - - var data = Base58.base58CheckDecode(wif); - return Arrays.copyOfRange(data, 1, data.length - 1); - } - - public static byte[] loadPublicKey(byte[] privateKey) { - checkInputValue(privateKey); - - X9ECParameters params = SECNamedCurves.getByOID(SECObjectIdentifiers.secp256r1); - ECDomainParameters domain = new ECDomainParameters( - params.getCurve(), params.getG(), params.getN(), params.getH() - ); - ECPoint q = domain.getG().multiply(new BigInteger(1, privateKey)); - ECPublicKeyParameters publicParams = new ECPublicKeyParameters(q, domain); - return publicParams.getQ().getEncoded(true); - } - public static PrivateKey loadPrivateKey(byte[] privateKey) { checkInputValue(privateKey); @@ -134,58 +85,6 @@ public class KeyExtension { } } - public static byte[] getScriptHash(byte[] publicKey) { - checkInputValue(publicKey); - - var script = createSignatureRedeemScript(publicKey); - return getRipemd160(getSha256(script)); - } - - public static String publicKeyToAddress(byte[] publicKey) { - checkInputValue(publicKey); - if (publicKey.length != COMPRESSED_PUBLIC_KEY_LENGTH) { - throw new ValidationFrostFSException(String.format( - ENCODED_COMPRESSED_PUBLIC_KEY_WRONG_LENGTH_TEMPLATE, COMPRESSED_PUBLIC_KEY_LENGTH, publicKey.length - )); - } - - return toAddress(getScriptHash(publicKey)); - } - - private static String toAddress(byte[] scriptHash) { - checkInputValue(scriptHash); - byte[] data = new byte[DECODE_ADDRESS_LENGTH]; - data[0] = NEO_ADDRESS_VERSION; - System.arraycopy(scriptHash, 0, data, 1, scriptHash.length); - return Base58.base58CheckEncode(data); - } - - private static byte[] getBytes(int value) { - byte[] buffer = new byte[4]; - - for (int i = 0; i < buffer.length; i++) { - buffer[i] = (byte) (value >> i * Byte.SIZE); - } - - return buffer; - } - - private static byte[] createSignatureRedeemScript(byte[] publicKey) { - checkInputValue(publicKey); - if (publicKey.length != COMPRESSED_PUBLIC_KEY_LENGTH) { - throw new ValidationFrostFSException(String.format( - ENCODED_COMPRESSED_PUBLIC_KEY_WRONG_LENGTH_TEMPLATE, COMPRESSED_PUBLIC_KEY_LENGTH, publicKey.length - )); - } - - var script = new byte[]{PS_IN_HASH160, COMPRESSED_PUBLIC_KEY_LENGTH}; //PUSHDATA1 33 - - script = ArrayHelper.concat(script, publicKey); - script = ArrayHelper.concat(script, new byte[]{UNCOMPRESSED_PUBLIC_KEY_LENGTH}); //SYSCALL - script = ArrayHelper.concat(script, getBytes(CHECK_SIG_DESCRIPTOR)); //Neo_Crypto_CheckSig - return script; - } - private static void checkInputValue(byte[] data) { if (isNull(data) || data.length == 0) { throw new ValidationFrostFSException(INPUT_PARAM_IS_MISSING); diff --git a/cryptography/src/test/java/info/frostfs/sdk/Base58Test.java b/cryptography/src/test/java/info/frostfs/sdk/Base58Test.java deleted file mode 100644 index bebcae4..0000000 --- a/cryptography/src/test/java/info/frostfs/sdk/Base58Test.java +++ /dev/null @@ -1,56 +0,0 @@ -package info.frostfs.sdk; - -import info.frostfs.sdk.exceptions.ProcessFrostFSException; -import info.frostfs.sdk.exceptions.ValidationFrostFSException; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - -public class Base58Test { - private static final String WIF = "L1YS4myg3xHPvi3FHeLaEt7G8upwJaWL5YLV7huviuUtXFpzBMqZ"; - private static final byte[] DECODE = new byte[]{ - -128, -128, -5, 30, -36, -118, 85, -67, -6, 81, 43, 93, -38, 106, 21, -88, 127, 15, 125, -79, -17, -40, 77, - -15, 122, -88, 72, 109, -47, 125, -80, -40, -38, 1 - }; - - @Test - void base58DecodeEncode() { - //When + Then - assertEquals(WIF, Base58.base58CheckEncode(Base58.base58CheckDecode(WIF))); - } - - @Test - void base58Decode_success() { - //When - var decode = Base58.base58CheckDecode(WIF); - - //Then - assertThat(decode).hasSize(34).containsExactly(DECODE); - } - - @Test - void base58Decode_wrong() { - //When + Then - assertThrows(ValidationFrostFSException.class, () -> Base58.base58CheckDecode(null)); - assertThrows(ValidationFrostFSException.class, () -> Base58.base58CheckDecode("")); - assertThrows(ValidationFrostFSException.class, () -> Base58.base58CheckDecode("WIF")); - assertThrows(ProcessFrostFSException.class, () -> Base58.base58CheckDecode("fh")); - } - - @Test - void base58Encode_success() { - //When - var encode = Base58.base58CheckEncode(DECODE); - - //Then - assertEquals(WIF, encode); - } - - @Test - void base58Encode_wrong() { - //When + Then - assertThrows(ValidationFrostFSException.class, () -> Base58.base58CheckEncode(null)); - } -} diff --git a/cryptography/src/test/java/info/frostfs/sdk/HelperTest.java b/cryptography/src/test/java/info/frostfs/sdk/HelperTest.java index 536e881..00a9121 100644 --- a/cryptography/src/test/java/info/frostfs/sdk/HelperTest.java +++ b/cryptography/src/test/java/info/frostfs/sdk/HelperTest.java @@ -10,38 +10,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows; public class HelperTest { - @Test - void getRipemd160_success() { - //Given - var value = new byte[]{1, 2, 3, 4, 5}; - var expected = new byte[] - {-21, -126, 92, 75, 36, -12, 37, 7, 122, 6, 124, -61, -66, -12, 87, 120, 63, 90, -41, 5}; - //When - var result = Helper.getRipemd160(value); - - //Then - assertThat(result).hasSize(20).containsExactly(expected); - } - - @Test - void getRipemd160_givenParamIsNull() { - //When + Then - assertThrows(ValidationFrostFSException.class, () -> Helper.getRipemd160(null)); - } - - @Test - void getRipemd160_givenParamsIsEmpty() { - //Given - var value = new byte[]{}; - var expected = new byte[] - {-100, 17, -123, -91, -59, -23, -4, 84, 97, 40, 8, -105, 126, -24, -11, 72, -78, 37, -115, 49}; - //When - var result = Helper.getRipemd160(value); - - //Then - assertThat(result).hasSize(20).containsExactly(expected); - } - @Test void getSha256Instance() { //When diff --git a/cryptography/src/test/java/info/frostfs/sdk/KeyExtensionTest.java b/cryptography/src/test/java/info/frostfs/sdk/KeyExtensionTest.java index 2f9b1b9..a99fe4d 100644 --- a/cryptography/src/test/java/info/frostfs/sdk/KeyExtensionTest.java +++ b/cryptography/src/test/java/info/frostfs/sdk/KeyExtensionTest.java @@ -8,8 +8,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; public class KeyExtensionTest { - private static final String WIF = "L1YS4myg3xHPvi3FHeLaEt7G8upwJaWL5YLV7huviuUtXFpzBMqZ"; - private static final String OWNER_ID = "NVxUSpEEJzYXZZtUs18PrJTD9QZkLLNQ8S"; private static final byte[] PRIVATE_KEY = new byte[]{ -128, -5, 30, -36, -118, 85, -67, -6, 81, 43, 93, -38, 106, 21, -88, 127, 15, 125, -79, -17, -40, 77, -15, 122, -88, 72, 109, -47, 125, -80, -40, -38 @@ -24,38 +22,6 @@ public class KeyExtensionTest { -68, -73, 65, -57, -26, 75, 4, -51, -40, -20, 75, 89, -59, 111, 96, -80, 56, 13 }; - @Test - void getPrivateKeyFromWIF_success() { - //When - var privateKey = KeyExtension.getPrivateKeyFromWIF(WIF); - - //Then - assertThat(privateKey).hasSize(32).containsExactly(PRIVATE_KEY); - } - - @Test - void getPrivateKeyFromWIF_wrong() { - //When + Then - assertThrows(ValidationFrostFSException.class, () -> KeyExtension.getPrivateKeyFromWIF("")); - assertThrows(ValidationFrostFSException.class, () -> KeyExtension.getPrivateKeyFromWIF(null)); - } - - @Test - void loadPublicKey_success() { - //When - var publicKey = KeyExtension.loadPublicKey(PRIVATE_KEY); - - //Then - assertThat(publicKey).hasSize(33).containsExactly(PUBLIC_KEY); - } - - @Test - void loadPublicKey_wrong() { - //When + Then - assertThrows(ValidationFrostFSException.class, () -> KeyExtension.loadPublicKey(null)); - assertThrows(ValidationFrostFSException.class, () -> KeyExtension.loadPublicKey(new byte[]{})); - } - @Test void loadPrivateKey_success() { //When @@ -92,61 +58,4 @@ public class KeyExtensionTest { assertThrows(ValidationFrostFSException.class, () -> KeyExtension.getPublicKeyFromBytes(new byte[]{})); assertThrows(ValidationFrostFSException.class, () -> KeyExtension.getPublicKeyFromBytes(PRIVATE_KEY)); } - - @Test - void getScriptHash_success() { - //Given - var expected = new byte[]{ - 110, 42, -125, -76, -25, -44, -94, 22, -98, 117, -100, -5, 103, 74, -128, -51, 37, -116, -102, 71 - }; - - //When - var hash = KeyExtension.getScriptHash(PUBLIC_KEY); - - //Then - assertThat(hash).hasSize(20).containsExactly(expected); - } - - @Test - void getScriptHash_wrong() { - //When + Then - assertThrows(ValidationFrostFSException.class, () -> KeyExtension.getScriptHash(null)); - assertThrows(ValidationFrostFSException.class, () -> KeyExtension.getScriptHash(new byte[]{})); - assertThrows(ValidationFrostFSException.class, () -> KeyExtension.getScriptHash(PRIVATE_KEY)); - } - - @Test - void publicKeyToAddress_success() { - //When - var address = KeyExtension.publicKeyToAddress(PUBLIC_KEY); - - //Then - assertEquals(OWNER_ID, address); - } - - @Test - void publicKeyToAddress_wrong() { - //When + Then - assertThrows(ValidationFrostFSException.class, () -> KeyExtension.publicKeyToAddress(null)); - assertThrows(ValidationFrostFSException.class, () -> KeyExtension.publicKeyToAddress(new byte[]{})); - assertThrows(ValidationFrostFSException.class, () -> KeyExtension.publicKeyToAddress(PRIVATE_KEY)); - } - - @Test - void compress_success() { - //When - var publicKey = KeyExtension.compress(UNCOMPRESSED_PUBLIC_KEY); - - //Then - assertThat(publicKey).hasSize(33).containsExactly(PUBLIC_KEY); - } - - @Test - void compress_wrong() { - //When + Then - assertThrows(ValidationFrostFSException.class, () -> KeyExtension.compress(null)); - assertThrows(ValidationFrostFSException.class, () -> KeyExtension.compress(new byte[]{})); - assertThrows(ValidationFrostFSException.class, () -> KeyExtension.compress(PUBLIC_KEY)); - } - } diff --git a/exceptions/src/main/java/info/frostfs/sdk/constants/ErrorConst.java b/exceptions/src/main/java/info/frostfs/sdk/constants/ErrorConst.java index 629943e..c9f65bc 100644 --- a/exceptions/src/main/java/info/frostfs/sdk/constants/ErrorConst.java +++ b/exceptions/src/main/java/info/frostfs/sdk/constants/ErrorConst.java @@ -20,6 +20,7 @@ public class ErrorConst { public static final String UNEXPECTED_MESSAGE_TYPE_TEMPLATE = "unexpected message type, expected %s, actually %s"; public static final String WIF_IS_INVALID = "WIF is invalid"; + public static final String WALLET_IS_INVALID = "wallet is not present"; public static final String UNEXPECTED_STREAM = "unexpected end of stream"; public static final String INVALID_HOST_TEMPLATE = "host %s has invalid format. Error: %s"; public static final String INVALID_RESPONSE = "invalid response"; @@ -28,14 +29,7 @@ public class ErrorConst { public static final String UNKNOWN_ENUM_VALUE_TEMPLATE = "unknown %s value: %s"; public static final String INPUT_PARAM_IS_NOT_SHA256 = "%s must be a sha256 hash"; - public static final String DECODE_LENGTH_VALUE_TEMPLATE = "decode array length must be >= 4, but %s"; - public static final String INVALID_BASE58_CHARACTER_TEMPLATE = "invalid character in Base58: 0x%04x"; - public static final String INVALID_CHECKSUM = "checksum does not match"; public static final String WRONG_SIGNATURE_SIZE_TEMPLATE = "wrong signature size. Expected length=%s, actual=%s"; - public static final String ENCODED_COMPRESSED_PUBLIC_KEY_WRONG_LENGTH_TEMPLATE = - "publicKey isn't encoded compressed public key. Expected length=%s, actual=%s"; - public static final String UNCOMPRESSED_PUBLIC_KEY_WRONG_LENGTH_TEMPLATE = - "compress argument isn't uncompressed public key. Expected length=%s, actual=%s"; public static final String COMPRESSED_PUBLIC_KEY_WRONG_LENGTH_TEMPLATE = "decompress argument isn't compressed public key. Expected length=%s, actual=%s"; diff --git a/models/src/main/java/info/frostfs/sdk/dto/container/ContainerId.java b/models/src/main/java/info/frostfs/sdk/dto/container/ContainerId.java index 393e3d0..def97fd 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/container/ContainerId.java +++ b/models/src/main/java/info/frostfs/sdk/dto/container/ContainerId.java @@ -1,8 +1,8 @@ package info.frostfs.sdk.dto.container; -import info.frostfs.sdk.Base58; import info.frostfs.sdk.constants.AppConst; import info.frostfs.sdk.exceptions.ValidationFrostFSException; +import io.neow3j.crypto.Base58; import lombok.Getter; import org.apache.commons.lang3.StringUtils; diff --git a/models/src/main/java/info/frostfs/sdk/dto/object/ObjectId.java b/models/src/main/java/info/frostfs/sdk/dto/object/ObjectId.java index 7f34f87..35e9ab2 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/object/ObjectId.java +++ b/models/src/main/java/info/frostfs/sdk/dto/object/ObjectId.java @@ -1,8 +1,8 @@ package info.frostfs.sdk.dto.object; -import info.frostfs.sdk.Base58; import info.frostfs.sdk.constants.AppConst; import info.frostfs.sdk.exceptions.ValidationFrostFSException; +import io.neow3j.crypto.Base58; import lombok.Getter; import org.apache.commons.lang3.StringUtils; diff --git a/models/src/main/java/info/frostfs/sdk/dto/object/OwnerId.java b/models/src/main/java/info/frostfs/sdk/dto/object/OwnerId.java index ba65652..82886e1 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/object/OwnerId.java +++ b/models/src/main/java/info/frostfs/sdk/dto/object/OwnerId.java @@ -1,14 +1,11 @@ package info.frostfs.sdk.dto.object; -import info.frostfs.sdk.Base58; import info.frostfs.sdk.exceptions.ValidationFrostFSException; +import io.neow3j.crypto.Base58; import lombok.Getter; import org.apache.commons.lang3.StringUtils; -import static info.frostfs.sdk.KeyExtension.publicKeyToAddress; -import static info.frostfs.sdk.constants.ErrorConst.INPUT_PARAM_IS_MISSING; import static info.frostfs.sdk.constants.ErrorConst.INPUT_PARAM_IS_MISSING_TEMPLATE; -import static java.util.Objects.isNull; @Getter public class OwnerId { @@ -24,14 +21,6 @@ public class OwnerId { this.value = value; } - public OwnerId(byte[] publicKey) { - if (isNull(publicKey) || publicKey.length == 0) { - throw new ValidationFrostFSException(INPUT_PARAM_IS_MISSING); - } - - this.value = publicKeyToAddress(publicKey); - } - public byte[] toHash() { return Base58.decode(value); } diff --git a/models/src/main/java/info/frostfs/sdk/enums/BasicAcl.java b/models/src/main/java/info/frostfs/sdk/enums/BasicAcl.java deleted file mode 100644 index 52bab99..0000000 --- a/models/src/main/java/info/frostfs/sdk/enums/BasicAcl.java +++ /dev/null @@ -1,33 +0,0 @@ -package info.frostfs.sdk.enums; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -public enum BasicAcl { - PRIVATE(0x1C8C8CCC), - PUBLIC_RO(0x1FBF8CFF), - PUBLIC_RW(0x1FBFBFFF), - PUBLIC_APPEND(0x1FBF9FFF), - ; - - private static final Map ENUM_MAP_BY_VALUE; - - static { - Map map = new HashMap<>(); - for (BasicAcl basicAcl : BasicAcl.values()) { - map.put(basicAcl.value, basicAcl); - } - ENUM_MAP_BY_VALUE = Collections.unmodifiableMap(map); - } - - public final int value; - - BasicAcl(int value) { - this.value = value; - } - - public static BasicAcl get(int value) { - return ENUM_MAP_BY_VALUE.get(value); - } -} diff --git a/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerMapper.java index 0571ff0..0c96e32 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/container/ContainerMapper.java @@ -3,8 +3,6 @@ package info.frostfs.sdk.mappers.container; import com.google.protobuf.ByteString; import frostfs.container.Types; import info.frostfs.sdk.dto.container.Container; -import info.frostfs.sdk.enums.BasicAcl; -import info.frostfs.sdk.exceptions.ProcessFrostFSException; import info.frostfs.sdk.mappers.netmap.PlacementPolicyMapper; import info.frostfs.sdk.mappers.netmap.VersionMapper; import info.frostfs.sdk.mappers.object.OwnerIdMapper; @@ -14,7 +12,6 @@ import java.util.stream.Collectors; import static info.frostfs.sdk.UuidExtension.asBytes; import static info.frostfs.sdk.UuidExtension.asUuid; -import static info.frostfs.sdk.constants.ErrorConst.UNKNOWN_ENUM_VALUE_TEMPLATE; import static java.util.Objects.isNull; public class ContainerMapper { @@ -51,13 +48,6 @@ public class ContainerMapper { return null; } - var basicAcl = BasicAcl.get(containerGrpc.getBasicAcl()); - if (isNull(basicAcl)) { - throw new ProcessFrostFSException( - String.format(UNKNOWN_ENUM_VALUE_TEMPLATE, BasicAcl.class.getName(), containerGrpc.getBasicAcl()) - ); - } - var attributes = containerGrpc.getAttributesList().stream() .collect(Collectors.toMap(Types.Container.Attribute::getKey, Types.Container.Attribute::getValue)); diff --git a/models/src/main/java/info/frostfs/sdk/mappers/object/OwnerIdMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/object/OwnerIdMapper.java index e1047b8..535905f 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/object/OwnerIdMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/object/OwnerIdMapper.java @@ -2,8 +2,8 @@ package info.frostfs.sdk.mappers.object; import com.google.protobuf.ByteString; import frostfs.refs.Types; -import info.frostfs.sdk.Base58; import info.frostfs.sdk.dto.object.OwnerId; +import io.neow3j.crypto.Base58; import static java.util.Objects.isNull; diff --git a/models/src/test/java/info/frostfs/sdk/mappers/container/ContainerMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/container/ContainerMapperTest.java index fadde6a..c390e40 100644 --- a/models/src/test/java/info/frostfs/sdk/mappers/container/ContainerMapperTest.java +++ b/models/src/test/java/info/frostfs/sdk/mappers/container/ContainerMapperTest.java @@ -7,12 +7,8 @@ import info.frostfs.sdk.dto.netmap.PlacementPolicy; import info.frostfs.sdk.dto.netmap.Replica; import info.frostfs.sdk.dto.netmap.Version; import info.frostfs.sdk.dto.object.OwnerId; -import info.frostfs.sdk.enums.BasicAcl; -import info.frostfs.sdk.exceptions.ProcessFrostFSException; import info.frostfs.sdk.mappers.object.OwnerIdMapper; import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.EnumSource; import java.util.UUID; @@ -106,9 +102,8 @@ public class ContainerMapperTest { assertNull(ContainerMapper.toGrpcMessage(null)); } - @ParameterizedTest - @EnumSource(value = BasicAcl.class) - void toModel_success(BasicAcl basicAcl) { + @Test + void toModel_success() { //Given var version = frostfs.refs.Types.Version.newBuilder() .setMajor(1) @@ -137,7 +132,6 @@ public class ContainerMapperTest { .build(); var container = Types.Container.newBuilder() - .setBasicAcl(basicAcl.value) .setNonce(ByteString.copyFrom(asBytes(UUID.randomUUID()))) .setVersion(version) .setPlacementPolicy(placementPolicy) @@ -178,15 +172,4 @@ public class ContainerMapperTest { assertNull(ContainerMapper.toModel(null)); assertNull(ContainerMapper.toModel(Types.Container.getDefaultInstance())); } - - @Test - void toModel_notValid() { - //Given - var container = Types.Container.newBuilder() - .setBasicAcl(-1) - .build(); - - //When + Then - assertThrows(ProcessFrostFSException.class, () -> ContainerMapper.toModel(container)); - } } diff --git a/pom.xml b/pom.xml index 8f58e1f..d2843a0 100644 --- a/pom.xml +++ b/pom.xml @@ -17,7 +17,7 @@ - 0.7.0 + 0.8.0 11 11 @@ -28,6 +28,7 @@ 3.26.3 1.18.34 3.23.0 + 3.23.0 @@ -48,6 +49,11 @@ provided true + + io.neow3j + contract + ${neow3j.version} + org.junit.jupiter junit-jupiter From 39158348dd3dee208af396f0f73e939b6f486e62 Mon Sep 17 00:00:00 2001 From: Ori Bruk Date: Wed, 5 Mar 2025 11:32:00 +0300 Subject: [PATCH 34/38] [#47] Add APE rule deserializer Signed-off-by: Ori Bruk --- CHANGELOG.md | 13 +- .../java/info/frostfs/sdk/FrostFSClient.java | 3 +- .../info/frostfs/sdk/constants/RuleConst.java | 5 +- .../main/java/info/frostfs/sdk/pool/Pool.java | 3 +- .../sdk/services/ApeManagerClient.java | 4 +- .../services/impl/ApeManagerClientImpl.java | 11 +- .../sdk/tools/{ => ape}/MarshalFunction.java | 2 +- .../sdk/tools/ape/RuleDeserializer.java | 198 ++++++++++++++++++ .../sdk/tools/{ => ape}/RuleSerializer.java | 6 +- .../sdk/tools/ape/UnmarshalFunction.java | 7 + .../test/java/info/frostfs/sdk/FileUtils.java | 31 --- .../sdk/services/ApeManagerClientTest.java | 3 +- .../testgenerator/ApeManagerGenerator.java | 7 +- .../frostfs/sdk/tools/ape/ApeRuleTest.java | 179 ++++++++++++++++ client/src/test/resources/test_chain_raw.json | 30 --- .../frostfs/sdk/constants/ErrorConst.java | 7 + .../frostfs/sdk/enums/ConditionKindType.java | 18 ++ .../info/frostfs/sdk/enums/ConditionType.java | 18 ++ .../info/frostfs/sdk/enums/RuleMatchType.java | 18 ++ .../info/frostfs/sdk/enums/RuleStatus.java | 18 ++ pom.xml | 2 +- 21 files changed, 504 insertions(+), 79 deletions(-) rename client/src/main/java/info/frostfs/sdk/tools/{ => ape}/MarshalFunction.java (70%) create mode 100644 client/src/main/java/info/frostfs/sdk/tools/ape/RuleDeserializer.java rename client/src/main/java/info/frostfs/sdk/tools/{ => ape}/RuleSerializer.java (97%) create mode 100644 client/src/main/java/info/frostfs/sdk/tools/ape/UnmarshalFunction.java delete mode 100644 client/src/test/java/info/frostfs/sdk/FileUtils.java create mode 100644 client/src/test/java/info/frostfs/sdk/tools/ape/ApeRuleTest.java delete mode 100644 client/src/test/resources/test_chain_raw.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 38ab6d8..389b85b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,26 +1,37 @@ # Changelog +## [0.9.0] - 2025-03-05 + +### Added + +- APE rule deserializer + ## [0.8.0] - 2025-03-04 ### Added + - Creating client via wallet and password ## [0.7.0] - 2025-02-20 ### Added + - Expanding the parameters for creating a container ### Fixed -- Creating a session for working with objects + +- Creating a session for working with objects ## [0.6.0] - 2025-02-13 ### Added + - APE rules serializer ## [0.5.0] - 2025-02-11 ### Fixed + - Loading large objects in chunks - .gitignore - pom revision \ No newline at end of file diff --git a/client/src/main/java/info/frostfs/sdk/FrostFSClient.java b/client/src/main/java/info/frostfs/sdk/FrostFSClient.java index c81c218..4aa685c 100644 --- a/client/src/main/java/info/frostfs/sdk/FrostFSClient.java +++ b/client/src/main/java/info/frostfs/sdk/FrostFSClient.java @@ -1,6 +1,7 @@ package info.frostfs.sdk; import frostfs.accounting.Types; +import info.frostfs.sdk.dto.ape.Chain; import info.frostfs.sdk.dto.container.Container; import info.frostfs.sdk.dto.container.ContainerId; import info.frostfs.sdk.dto.netmap.NetmapSnapshot; @@ -197,7 +198,7 @@ public class FrostFSClient implements CommonClient { } @Override - public List listChains(PrmApeChainList args, CallContext ctx) { + public List listChains(PrmApeChainList args, CallContext ctx) { return apeManagerClient.listChains(args, ctx); } diff --git a/client/src/main/java/info/frostfs/sdk/constants/RuleConst.java b/client/src/main/java/info/frostfs/sdk/constants/RuleConst.java index 78e656f..322b615 100644 --- a/client/src/main/java/info/frostfs/sdk/constants/RuleConst.java +++ b/client/src/main/java/info/frostfs/sdk/constants/RuleConst.java @@ -17,10 +17,13 @@ public class RuleConst { // https://github.com/neo-project/neo/blob/38218bbee5bbe8b33cd8f9453465a19381c9a547/src/Neo/IO/Helper.cs#L77 public static final int MAX_SLICE_LENGTH = 0x1000000; + public static final int MAX_VAR_INT_LENGTH = 10; + public static final int CHAIN_MARSHAL_VERSION = 0; + public static final long OFFSET127 = 0x7f; public static final long OFFSET128 = 0x80; - public static final long UNSIGNED_SERIALIZE_SIZE = 7; + public static final int UNSIGNED_SERIALIZE_SIZE = 7; private RuleConst() { } diff --git a/client/src/main/java/info/frostfs/sdk/pool/Pool.java b/client/src/main/java/info/frostfs/sdk/pool/Pool.java index d8bac59..2191643 100644 --- a/client/src/main/java/info/frostfs/sdk/pool/Pool.java +++ b/client/src/main/java/info/frostfs/sdk/pool/Pool.java @@ -1,6 +1,7 @@ package info.frostfs.sdk.pool; import frostfs.refs.Types; +import info.frostfs.sdk.dto.ape.Chain; import info.frostfs.sdk.dto.container.Container; import info.frostfs.sdk.dto.container.ContainerId; import info.frostfs.sdk.dto.netmap.NetmapSnapshot; @@ -514,7 +515,7 @@ public class Pool implements CommonClient { } @Override - public List listChains(PrmApeChainList args, CallContext ctx) { + public List listChains(PrmApeChainList args, CallContext ctx) { ClientWrapper client = connection(); return client.getClient().listChains(args, ctx); } diff --git a/client/src/main/java/info/frostfs/sdk/services/ApeManagerClient.java b/client/src/main/java/info/frostfs/sdk/services/ApeManagerClient.java index 965e458..47d7827 100644 --- a/client/src/main/java/info/frostfs/sdk/services/ApeManagerClient.java +++ b/client/src/main/java/info/frostfs/sdk/services/ApeManagerClient.java @@ -1,6 +1,6 @@ package info.frostfs.sdk.services; -import frostfs.ape.Types; +import info.frostfs.sdk.dto.ape.Chain; import info.frostfs.sdk.jdo.parameters.CallContext; import info.frostfs.sdk.jdo.parameters.ape.PrmApeChainAdd; import info.frostfs.sdk.jdo.parameters.ape.PrmApeChainList; @@ -13,5 +13,5 @@ public interface ApeManagerClient { void removeChain(PrmApeChainRemove args, CallContext ctx); - List listChains(PrmApeChainList args, CallContext ctx); + List listChains(PrmApeChainList args, CallContext ctx); } diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/ApeManagerClientImpl.java b/client/src/main/java/info/frostfs/sdk/services/impl/ApeManagerClientImpl.java index cd81060..6e10fe3 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/ApeManagerClientImpl.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/ApeManagerClientImpl.java @@ -4,6 +4,7 @@ import com.google.protobuf.ByteString; import frostfs.ape.Types; import frostfs.apemanager.APEManagerServiceGrpc; import frostfs.apemanager.Service; +import info.frostfs.sdk.dto.ape.Chain; import info.frostfs.sdk.jdo.ClientEnvironment; import info.frostfs.sdk.jdo.parameters.CallContext; import info.frostfs.sdk.jdo.parameters.ape.PrmApeChainAdd; @@ -14,10 +15,12 @@ import info.frostfs.sdk.services.ApeManagerClient; import info.frostfs.sdk.services.ContextAccessor; import info.frostfs.sdk.tools.RequestConstructor; import info.frostfs.sdk.tools.RequestSigner; -import info.frostfs.sdk.tools.RuleSerializer; import info.frostfs.sdk.tools.Verifier; +import info.frostfs.sdk.tools.ape.RuleDeserializer; +import info.frostfs.sdk.tools.ape.RuleSerializer; import java.util.List; +import java.util.stream.Collectors; import static info.frostfs.sdk.utils.DeadLineUtil.deadLineAfter; import static info.frostfs.sdk.utils.Validator.validate; @@ -57,7 +60,7 @@ public class ApeManagerClientImpl extends ContextAccessor implements ApeManagerC } @Override - public List listChains(PrmApeChainList args, CallContext ctx) { + public List listChains(PrmApeChainList args, CallContext ctx) { validate(args); var request = createListChainsRequest(args); @@ -67,7 +70,9 @@ public class ApeManagerClientImpl extends ContextAccessor implements ApeManagerC Verifier.checkResponse(response); - return response.getBody().getChainsList(); + return response.getBody().getChainsList().stream() + .map(chain -> RuleDeserializer.deserialize(chain.getRaw().toByteArray())) + .collect(Collectors.toList()); } private Service.AddChainRequest createAddChainRequest(PrmApeChainAdd args) { diff --git a/client/src/main/java/info/frostfs/sdk/tools/MarshalFunction.java b/client/src/main/java/info/frostfs/sdk/tools/ape/MarshalFunction.java similarity index 70% rename from client/src/main/java/info/frostfs/sdk/tools/MarshalFunction.java rename to client/src/main/java/info/frostfs/sdk/tools/ape/MarshalFunction.java index accd7b1..1b08d5c 100644 --- a/client/src/main/java/info/frostfs/sdk/tools/MarshalFunction.java +++ b/client/src/main/java/info/frostfs/sdk/tools/ape/MarshalFunction.java @@ -1,4 +1,4 @@ -package info.frostfs.sdk.tools; +package info.frostfs.sdk.tools.ape; public interface MarshalFunction { int marshal(byte[] buf, int offset, T t); diff --git a/client/src/main/java/info/frostfs/sdk/tools/ape/RuleDeserializer.java b/client/src/main/java/info/frostfs/sdk/tools/ape/RuleDeserializer.java new file mode 100644 index 0000000..cbba2e2 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/tools/ape/RuleDeserializer.java @@ -0,0 +1,198 @@ +package info.frostfs.sdk.tools.ape; + +import info.frostfs.sdk.dto.ape.*; +import info.frostfs.sdk.enums.ConditionKindType; +import info.frostfs.sdk.enums.ConditionType; +import info.frostfs.sdk.enums.RuleMatchType; +import info.frostfs.sdk.enums.RuleStatus; +import info.frostfs.sdk.exceptions.FrostFSException; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; +import org.apache.commons.lang3.ArrayUtils; + +import java.lang.reflect.Array; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.atomic.AtomicInteger; + +import static info.frostfs.sdk.constants.ErrorConst.*; +import static info.frostfs.sdk.constants.FieldConst.EMPTY_STRING; +import static info.frostfs.sdk.constants.RuleConst.*; + +public class RuleDeserializer { + private RuleDeserializer() { + } + + public static Chain deserialize(byte[] data) { + if (ArrayUtils.isEmpty(data)) { + throw new ValidationFrostFSException(INPUT_PARAM_IS_MISSING); + } + + AtomicInteger offset = new AtomicInteger(0); + Chain chain = new Chain(); + + var version = uInt8Unmarshal(data, offset); + if (version != VERSION) { + throw new FrostFSException(String.format(UNSUPPORTED_MARSHALLER_VERSION_TEMPLATE, version)); + } + + var chainVersion = uInt8Unmarshal(data, offset); + if (chainVersion != CHAIN_MARSHAL_VERSION) { + throw new FrostFSException(String.format(UNSUPPORTED_CHAIN_VERSION_TEMPLATE, chainVersion)); + } + + chain.setId(sliceUnmarshal(data, offset, Byte.class, RuleDeserializer::uInt8Unmarshal)); + chain.setRules(sliceUnmarshal(data, offset, Rule.class, RuleDeserializer::unmarshalRule)); + chain.setMatchType(RuleMatchType.get(uInt8Unmarshal(data, offset))); + + verifyUnmarshal(data, offset); + + return chain; + } + + private static Byte uInt8Unmarshal(byte[] buf, AtomicInteger offset) { + if (buf.length - offset.get() < 1) { + throw new FrostFSException( + String.format(BYTES_ARE_OVER_FOR_DESERIALIZE_TEMPLATE, Byte.class.getName(), offset.get())); + } + + return buf[offset.getAndAdd(1)]; + } + + public static long varInt(byte[] buf, AtomicInteger offset) { + long ux = uVarInt(buf, offset); // ok to continue in presence of error + long x = ux >> 1; + if ((ux & 1) != 0) { + x = ~x; + } + return x; + } + + + public static long uVarInt(byte[] buf, AtomicInteger offset) { + long x = 0; + int s = 0; + + for (int i = offset.get(); i < buf.length; i++) { + long b = buf[i]; + if (i == MAX_VAR_INT_LENGTH) { + offset.set(-(i + 1)); + return 0; // overflow + } + if (b >= 0) { + if (i == MAX_VAR_INT_LENGTH - 1 && b > 1) { + offset.set(-(i + 1)); + return 0; // overflow + } + offset.set(i + 1); + return x | (b << s); + } + x |= (b & OFFSET127) << s; + s += UNSIGNED_SERIALIZE_SIZE; + } + offset.set(0); + return 0; + } + + private static T[] sliceUnmarshal(byte[] buf, + AtomicInteger offset, + Class clazz, + UnmarshalFunction unmarshalT) { + var size = (int) varInt(buf, offset); + if (size == NULL_SLICE) { + return null; + } + + if (size > MAX_SLICE_LENGTH) { + throw new ValidationFrostFSException(String.format(SLICE_IS_TOO_BIG_TEMPLATE, size)); + } + + if (size < 0) { + throw new ValidationFrostFSException(String.format(SLICE_SIZE_IS_INVALID_TEMPLATE, size)); + } + + T[] result = (T[]) Array.newInstance(clazz, size); + for (int i = 0; i < result.length; i++) { + result[i] = unmarshalT.unmarshal(buf, offset); + } + + return result; + } + + private static boolean boolUnmarshal(byte[] buf, AtomicInteger offset) { + return uInt8Unmarshal(buf, offset) == BYTE_TRUE; + } + + private static long int64Unmarshal(byte[] buf, AtomicInteger offset) { + if (buf.length - offset.get() < Long.BYTES) { + throw new ValidationFrostFSException( + String.format(BYTES_ARE_OVER_FOR_DESERIALIZE_TEMPLATE, Long.class.getName(), offset.get()) + ); + } + + return varInt(buf, offset); + } + + private static String stringUnmarshal(byte[] buf, AtomicInteger offset) { + int size = (int) int64Unmarshal(buf, offset); + if (size == 0) { + return EMPTY_STRING; + } + + if (size > MAX_SLICE_LENGTH) { + throw new ValidationFrostFSException(String.format(STRING_IS_TOO_BIG_TEMPLATE, size)); + } + + if (size < 0) { + throw new ValidationFrostFSException(String.format(STRING_SIZE_IS_INVALID_TEMPLATE, size)); + } + + if (buf.length - offset.get() < size) { + throw new ValidationFrostFSException( + String.format(BYTES_ARE_OVER_FOR_DESERIALIZE_TEMPLATE, String.class.getName(), offset.get()) + ); + } + + return new String(buf, offset.getAndAdd(size), size, StandardCharsets.UTF_8); + } + + private static Actions unmarshalActions(byte[] buf, AtomicInteger offset) { + Actions actions = new Actions(); + actions.setInverted(boolUnmarshal(buf, offset)); + actions.setNames(sliceUnmarshal(buf, offset, String.class, RuleDeserializer::stringUnmarshal)); + return actions; + } + + private static Condition unmarshalCondition(byte[] buf, AtomicInteger offset) { + Condition condition = new Condition(); + condition.setOp(ConditionType.get(uInt8Unmarshal(buf, offset))); + condition.setKind(ConditionKindType.get(uInt8Unmarshal(buf, offset))); + condition.setKey(stringUnmarshal(buf, offset)); + condition.setValue(stringUnmarshal(buf, offset)); + + return condition; + } + + private static Rule unmarshalRule(byte[] buf, AtomicInteger offset) { + Rule rule = new Rule(); + rule.setStatus(RuleStatus.get(uInt8Unmarshal(buf, offset))); + rule.setActions(unmarshalActions(buf, offset)); + rule.setResources(unmarshalResources(buf, offset)); + rule.setAny(boolUnmarshal(buf, offset)); + rule.setConditions(sliceUnmarshal(buf, offset, Condition.class, RuleDeserializer::unmarshalCondition)); + + return rule; + } + + private static Resources unmarshalResources(byte[] buf, AtomicInteger offset) { + Resources resources = new Resources(); + resources.setInverted(boolUnmarshal(buf, offset)); + resources.setNames(sliceUnmarshal(buf, offset, String.class, RuleDeserializer::stringUnmarshal)); + + return resources; + } + + private static void verifyUnmarshal(byte[] buf, AtomicInteger offset) { + if (buf.length != offset.get()) { + throw new ValidationFrostFSException(UNMARSHAL_SIZE_DIFFERS); + } + } +} diff --git a/client/src/main/java/info/frostfs/sdk/tools/RuleSerializer.java b/client/src/main/java/info/frostfs/sdk/tools/ape/RuleSerializer.java similarity index 97% rename from client/src/main/java/info/frostfs/sdk/tools/RuleSerializer.java rename to client/src/main/java/info/frostfs/sdk/tools/ape/RuleSerializer.java index 097708d..43e7210 100644 --- a/client/src/main/java/info/frostfs/sdk/tools/RuleSerializer.java +++ b/client/src/main/java/info/frostfs/sdk/tools/ape/RuleSerializer.java @@ -1,4 +1,4 @@ -package info.frostfs.sdk.tools; +package info.frostfs.sdk.tools.ape; import info.frostfs.sdk.dto.ape.*; import info.frostfs.sdk.exceptions.ValidationFrostFSException; @@ -17,6 +17,10 @@ public class RuleSerializer { } public static byte[] serialize(Chain chain) { + if (isNull(chain)) { + throw new ValidationFrostFSException(String.format(INPUT_PARAM_IS_MISSING_TEMPLATE, Chain.class.getName())); + } + int s = U_INT_8_SIZE // Marshaller version + U_INT_8_SIZE // Chain version + sliceSize(chain.getId(), b -> BYTE_SIZE) diff --git a/client/src/main/java/info/frostfs/sdk/tools/ape/UnmarshalFunction.java b/client/src/main/java/info/frostfs/sdk/tools/ape/UnmarshalFunction.java new file mode 100644 index 0000000..ce0977e --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/tools/ape/UnmarshalFunction.java @@ -0,0 +1,7 @@ +package info.frostfs.sdk.tools.ape; + +import java.util.concurrent.atomic.AtomicInteger; + +public interface UnmarshalFunction { + T unmarshal(byte[] buf, AtomicInteger offset); +} diff --git a/client/src/test/java/info/frostfs/sdk/FileUtils.java b/client/src/test/java/info/frostfs/sdk/FileUtils.java deleted file mode 100644 index 0eb9e80..0000000 --- a/client/src/test/java/info/frostfs/sdk/FileUtils.java +++ /dev/null @@ -1,31 +0,0 @@ -package info.frostfs.sdk; - -import com.google.common.io.ByteStreams; -import lombok.SneakyThrows; -import org.apache.commons.lang3.StringUtils; - -import java.io.InputStream; - -import static java.util.Objects.isNull; - -public class FileUtils { - - @SneakyThrows - public static byte[] resourceToBytes(String resourcePath) { - if (StringUtils.isBlank(resourcePath)) { - throw new IllegalArgumentException("Blank filename!"); - } - - ClassLoader loader = FileUtils.class.getClassLoader(); - if (isNull(loader)) { - throw new RuntimeException("Class loader is null!"); - } - - InputStream certStream = loader.getResourceAsStream(resourcePath); - if (isNull(certStream)) { - throw new RuntimeException("Resource could not be found!"); - } - - return ByteStreams.toByteArray(certStream); - } -} diff --git a/client/src/test/java/info/frostfs/sdk/services/ApeManagerClientTest.java b/client/src/test/java/info/frostfs/sdk/services/ApeManagerClientTest.java index 5e62c74..d6a4e4b 100644 --- a/client/src/test/java/info/frostfs/sdk/services/ApeManagerClientTest.java +++ b/client/src/test/java/info/frostfs/sdk/services/ApeManagerClientTest.java @@ -208,8 +208,7 @@ class ApeManagerClientTest { ); verifierMock.verify(() -> Verifier.checkResponse(response), times(1)); - var expected = response.getBody().getChainsList(); - assertThat(result).hasSize(10).containsAll(expected); + assertThat(result).hasSize(10); var request = captor.getValue(); assertEquals(chainTarget.getName(), request.getBody().getTarget().getName()); diff --git a/client/src/test/java/info/frostfs/sdk/testgenerator/ApeManagerGenerator.java b/client/src/test/java/info/frostfs/sdk/testgenerator/ApeManagerGenerator.java index 1a9e497..a050114 100644 --- a/client/src/test/java/info/frostfs/sdk/testgenerator/ApeManagerGenerator.java +++ b/client/src/test/java/info/frostfs/sdk/testgenerator/ApeManagerGenerator.java @@ -3,22 +3,21 @@ package info.frostfs.sdk.testgenerator; import com.google.protobuf.ByteString; import frostfs.ape.Types; import frostfs.apemanager.Service; -import info.frostfs.sdk.FileUtils; import info.frostfs.sdk.Helper; +import org.bouncycastle.util.encoders.Base64; import java.util.ArrayList; public class ApeManagerGenerator { + private static final String BASE64_CHAIN = "AAAaY2hhaW4taWQtdGVzdAIAAAICKgACHm5hdGl2ZTpvYmplY3QvKgAAAA=="; private static ByteString generateChainID() { return ByteString.copyFrom(Helper.getByteArrayFromHex("616c6c6f774f626a476574436e72")); } private static Types.Chain generateRawChain() { - byte[] chainRaw = FileUtils.resourceToBytes("test_chain_raw.json"); - return Types.Chain.newBuilder() - .setRaw(ByteString.copyFrom(chainRaw)) + .setRaw(ByteString.copyFrom(Base64.decode(BASE64_CHAIN))) .build(); } diff --git a/client/src/test/java/info/frostfs/sdk/tools/ape/ApeRuleTest.java b/client/src/test/java/info/frostfs/sdk/tools/ape/ApeRuleTest.java new file mode 100644 index 0000000..e3df60c --- /dev/null +++ b/client/src/test/java/info/frostfs/sdk/tools/ape/ApeRuleTest.java @@ -0,0 +1,179 @@ +package info.frostfs.sdk.tools.ape; + +import info.frostfs.sdk.dto.ape.*; +import info.frostfs.sdk.enums.ConditionKindType; +import info.frostfs.sdk.enums.ConditionType; +import info.frostfs.sdk.enums.RuleMatchType; +import info.frostfs.sdk.enums.RuleStatus; +import info.frostfs.sdk.exceptions.FrostFSException; +import info.frostfs.sdk.exceptions.ValidationFrostFSException; +import org.apache.commons.lang3.ArrayUtils; +import org.bouncycastle.util.encoders.Base64; +import org.junit.jupiter.api.Test; + +import java.nio.charset.StandardCharsets; + +import static java.util.Objects.isNull; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; + +public class ApeRuleTest { + + @Test + void apeRuleTest() { + //Given + var resources = new Resources(false, new String[]{"native:object/*"}); + var actions = new Actions(false, new String[]{"*"}); + var rule = new Rule(); + rule.setStatus(RuleStatus.ALLOW); + rule.setResources(resources); + rule.setActions(actions); + rule.setAny(false); + rule.setConditions(new Condition[]{}); + + + var chain = new Chain(); + chain.setId(ArrayUtils.toObject("chain-id-test".getBytes(StandardCharsets.UTF_8))); + chain.setRules(new Rule[]{rule}); + chain.setMatchType(RuleMatchType.DENY_PRIORITY); + + //When + var serialized = RuleSerializer.serialize(chain); + var t = Base64.encode(serialized); + var restoredChain = RuleDeserializer.deserialize(serialized); + + //Then + assertThat(restoredChain.getId()).isNotEmpty().containsOnly(chain.getId()); + assertEquals(chain.getMatchType(), restoredChain.getMatchType()); + compareRules(chain.getRules(), restoredChain.getRules()); + } + + @Test + void apeRuleTest2() { + //Given + var resources = new Resources(true, new String[]{"native:object/*", "U.S.S. ENTERPRISE"}); + var actions = new Actions(true, new String[]{"put", "get"}); + var cond1 = new Condition( + ConditionType.COND_STRING_EQUALS, ConditionKindType.RESOURCE, "key1", "value1" + ); + var cond2 = new Condition( + ConditionType.COND_NUMERIC_GREATER_THAN, ConditionKindType.REQUEST, "key2", "value2" + ); + var rule = new Rule(); + rule.setStatus(RuleStatus.ACCESS_DENIED); + rule.setResources(resources); + rule.setActions(actions); + rule.setAny(true); + rule.setConditions(new Condition[]{cond1, cond2}); + + + var chain = new Chain(); + chain.setId(ArrayUtils.toObject("dumptext".getBytes(StandardCharsets.UTF_8))); + chain.setRules(new Rule[]{rule}); + chain.setMatchType(RuleMatchType.FIRST_MATCH); + + //When + var serialized = RuleSerializer.serialize(chain); + var restoredChain = RuleDeserializer.deserialize(serialized); + + //Then + assertThat(restoredChain.getId()).isNotEmpty().containsOnly(chain.getId()); + assertEquals(chain.getMatchType(), restoredChain.getMatchType()); + compareRules(chain.getRules(), restoredChain.getRules()); + } + + @Test + void apeRuleTest3() { + //Given + var chain = new Chain(); + chain.setMatchType(RuleMatchType.DENY_PRIORITY); + + //When + var serialized = RuleSerializer.serialize(chain); + var restoredChain = RuleDeserializer.deserialize(serialized); + + //Then + assertNull(restoredChain.getId()); + assertEquals(chain.getMatchType(), restoredChain.getMatchType()); + assertNull(restoredChain.getRules()); + } + + @Test + void apeRule_deserialize_wrong() { + //When + Then + assertThrows(ValidationFrostFSException.class, () -> RuleDeserializer.deserialize(null)); + assertThrows(ValidationFrostFSException.class, () -> RuleDeserializer.deserialize(new byte[]{})); + assertThrows(FrostFSException.class, () -> RuleDeserializer.deserialize(new byte[]{1, 2, 3})); + assertThrows(ValidationFrostFSException.class, () -> RuleDeserializer.deserialize(new byte[]{ + 0x00, 0x00, 0x3A, 0x77, 0x73, 0x3A, 0x69, 0x61, 0x6D, 0x3A, 0x3A, 0x6E, 0x61, 0x6D, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x3A, 0x67, 0x72, 0x6F, 0x75, 0x70, 0x2F, 0x73, 0x6F, (byte) 0x82, (byte) 0x82, + (byte) 0x82, (byte) 0x82, (byte) 0x82, (byte) 0x82, 0x75, (byte) 0x82 + })); + } + + @Test + void apeRule_serialize_wrong() { + //When + Then + assertThrows(ValidationFrostFSException.class, () -> RuleSerializer.serialize(null)); + } + + private void compareRules(Rule[] rules1, Rule[] rules2) { + assertThat(rules1).isNotEmpty(); + assertThat(rules2).isNotEmpty(); + + assertEquals(rules1.length, rules2.length); + + for (int ri = 0; ri < rules1.length; ri++) { + var rule1 = rules1[ri]; + var rule2 = rules2[ri]; + + assertEquals(rule1.getStatus(), rule2.getStatus()); + assertEquals(rule1.isAny(), rule2.isAny()); + + compareActions(rule1.getActions(), rule2.getActions()); + compareResources(rule1.getResources(), rule2.getResources()); + compareConditions(rule1.getConditions(), rule2.getConditions()); + } + } + + private void compareActions(Actions actions1, Actions actions2) { + if (isNull(actions1) && isNull(actions2)) { + return; + } + + assertEquals(actions1.isInverted(), actions2.isInverted()); + if (ArrayUtils.isEmpty(actions1.getNames()) && ArrayUtils.isEmpty(actions2.getNames())) { + return; + } + + assertThat(actions2.getNames()).hasSize(actions1.getNames().length).containsOnly(actions1.getNames()); + } + + private void compareResources(Resources resources1, Resources resources2) { + if (isNull(resources1) && isNull(resources2)) { + return; + } + + assertEquals(resources1.isInverted(), resources2.isInverted()); + if (ArrayUtils.isEmpty(resources1.getNames()) && ArrayUtils.isEmpty(resources2.getNames())) { + return; + } + + assertThat(resources2.getNames()).hasSize(resources1.getNames().length).containsOnly(resources1.getNames()); + } + + private void compareConditions(Condition[] conditions1, Condition[] conditions2) { + if (ArrayUtils.isEmpty(conditions1) && ArrayUtils.isEmpty(conditions2)) { + return; + } + + assertEquals(conditions1.length, conditions2.length); + for (int i = 0; i < conditions1.length; i++) { + assertEquals(conditions1[i].getOp(), conditions2[i].getOp()); + assertEquals(conditions1[i].getKind(), conditions2[i].getKind()); + assertEquals(conditions1[i].getKey(), conditions2[i].getKey()); + assertEquals(conditions1[i].getValue(), conditions2[i].getValue()); + } + } + +} diff --git a/client/src/test/resources/test_chain_raw.json b/client/src/test/resources/test_chain_raw.json deleted file mode 100644 index 60dfc3b..0000000 --- a/client/src/test/resources/test_chain_raw.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "ID": "", - "Rules": [ - { - "Status": "Allow", - "Actions": { - "Inverted": false, - "Names": [ - "GetObject" - ] - }, - "Resources": { - "Inverted": false, - "Names": [ - "native:object/*" - ] - }, - "Any": false, - "Condition": [ - { - "Op": "StringEquals", - "Object": "Resource", - "Key": "Department", - "Value": "HR" - } - ] - } - ], - "MatchType": "DenyPriority" -} \ No newline at end of file diff --git a/exceptions/src/main/java/info/frostfs/sdk/constants/ErrorConst.java b/exceptions/src/main/java/info/frostfs/sdk/constants/ErrorConst.java index c9f65bc..00c1711 100644 --- a/exceptions/src/main/java/info/frostfs/sdk/constants/ErrorConst.java +++ b/exceptions/src/main/java/info/frostfs/sdk/constants/ErrorConst.java @@ -46,11 +46,18 @@ public class ErrorConst { public static final String FIELDS_DELIMITER_COMMA = ", "; public static final String FIELDS_DELIMITER_OR = " or "; + public static final String UNSUPPORTED_MARSHALLER_VERSION_TEMPLATE = "unsupported marshaller version %s"; + public static final String UNSUPPORTED_CHAIN_VERSION_TEMPLATE = "unsupported chain version %s"; public static final String MARSHAL_SIZE_DIFFERS = "actual data size differs from expected"; + public static final String UNMARSHAL_SIZE_DIFFERS = "unmarshalled bytes left"; public static final String BYTES_ARE_OVER_FOR_SERIALIZE_TEMPLATE = "not enough bytes left to serialize value of type %s with length=%s"; + public static final String BYTES_ARE_OVER_FOR_DESERIALIZE_TEMPLATE = + "not enough bytes left to deserialize a value of type %s from offset=%s"; public static final String SLICE_IS_TOO_BIG_TEMPLATE = "slice size is too big=%s"; + public static final String SLICE_SIZE_IS_INVALID_TEMPLATE = "invalid slice size=%s"; public static final String STRING_IS_TOO_BIG_TEMPLATE = "string size is too big=%s"; + public static final String STRING_SIZE_IS_INVALID_TEMPLATE = "invalid string size=%s"; private ErrorConst() { } diff --git a/models/src/main/java/info/frostfs/sdk/enums/ConditionKindType.java b/models/src/main/java/info/frostfs/sdk/enums/ConditionKindType.java index a437254..9f1bc0e 100644 --- a/models/src/main/java/info/frostfs/sdk/enums/ConditionKindType.java +++ b/models/src/main/java/info/frostfs/sdk/enums/ConditionKindType.java @@ -1,13 +1,31 @@ package info.frostfs.sdk.enums; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + public enum ConditionKindType { RESOURCE(0), REQUEST(1), ; + private static final Map ENUM_MAP_BY_VALUE; + + static { + Map map = new HashMap<>(); + for (ConditionKindType conditionKindType : ConditionKindType.values()) { + map.put(conditionKindType.value, conditionKindType); + } + ENUM_MAP_BY_VALUE = Collections.unmodifiableMap(map); + } + public final int value; ConditionKindType(int value) { this.value = value; } + + public static ConditionKindType get(int value) { + return ENUM_MAP_BY_VALUE.get(value); + } } diff --git a/models/src/main/java/info/frostfs/sdk/enums/ConditionType.java b/models/src/main/java/info/frostfs/sdk/enums/ConditionType.java index b23417c..dca427d 100644 --- a/models/src/main/java/info/frostfs/sdk/enums/ConditionType.java +++ b/models/src/main/java/info/frostfs/sdk/enums/ConditionType.java @@ -1,5 +1,9 @@ package info.frostfs.sdk.enums; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + public enum ConditionType { COND_STRING_EQUALS(0), COND_STRING_NOT_EQUALS(1), @@ -28,9 +32,23 @@ public enum ConditionType { COND_NOT_IP_ADDRESS(18), ; + private static final Map ENUM_MAP_BY_VALUE; + + static { + Map map = new HashMap<>(); + for (ConditionType conditionType : ConditionType.values()) { + map.put(conditionType.value, conditionType); + } + ENUM_MAP_BY_VALUE = Collections.unmodifiableMap(map); + } + public final int value; ConditionType(int value) { this.value = value; } + + public static ConditionType get(int value) { + return ENUM_MAP_BY_VALUE.get(value); + } } diff --git a/models/src/main/java/info/frostfs/sdk/enums/RuleMatchType.java b/models/src/main/java/info/frostfs/sdk/enums/RuleMatchType.java index bfacaf6..6bbbe82 100644 --- a/models/src/main/java/info/frostfs/sdk/enums/RuleMatchType.java +++ b/models/src/main/java/info/frostfs/sdk/enums/RuleMatchType.java @@ -1,5 +1,9 @@ package info.frostfs.sdk.enums; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + public enum RuleMatchType { // DENY_PRIORITY rejects the request if any `Deny` is specified. DENY_PRIORITY(0), @@ -8,9 +12,23 @@ public enum RuleMatchType { FIRST_MATCH(1), ; + private static final Map ENUM_MAP_BY_VALUE; + + static { + Map map = new HashMap<>(); + for (RuleMatchType ruleMatchType : RuleMatchType.values()) { + map.put(ruleMatchType.value, ruleMatchType); + } + ENUM_MAP_BY_VALUE = Collections.unmodifiableMap(map); + } + public final int value; RuleMatchType(int value) { this.value = value; } + + public static RuleMatchType get(int value) { + return ENUM_MAP_BY_VALUE.get(value); + } } diff --git a/models/src/main/java/info/frostfs/sdk/enums/RuleStatus.java b/models/src/main/java/info/frostfs/sdk/enums/RuleStatus.java index b2f6cf1..45289d0 100644 --- a/models/src/main/java/info/frostfs/sdk/enums/RuleStatus.java +++ b/models/src/main/java/info/frostfs/sdk/enums/RuleStatus.java @@ -1,5 +1,9 @@ package info.frostfs.sdk.enums; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + public enum RuleStatus { ALLOW(0), NO_RULE_FOUND(1), @@ -7,9 +11,23 @@ public enum RuleStatus { QUOTA_LIMIT_REACHED(3), ; + private static final Map ENUM_MAP_BY_VALUE; + + static { + Map map = new HashMap<>(); + for (RuleStatus ruleStatus : RuleStatus.values()) { + map.put(ruleStatus.value, ruleStatus); + } + ENUM_MAP_BY_VALUE = Collections.unmodifiableMap(map); + } + public final int value; RuleStatus(int value) { this.value = value; } + + public static RuleStatus get(int value) { + return ENUM_MAP_BY_VALUE.get(value); + } } diff --git a/pom.xml b/pom.xml index d2843a0..01abab2 100644 --- a/pom.xml +++ b/pom.xml @@ -17,7 +17,7 @@ - 0.8.0 + 0.9.0 11 11 From 8b44db371430769c8ade6a718856a20bee9b2d38 Mon Sep 17 00:00:00 2001 From: Ori Bruk Date: Mon, 10 Mar 2025 13:42:11 +0300 Subject: [PATCH 35/38] [#49] Provide auto deploy after merge Signed-off-by: Ori Bruk --- pom.xml | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 01abab2..978fea2 100644 --- a/pom.xml +++ b/pom.xml @@ -17,7 +17,7 @@ - 0.9.0 + 0.10.0 11 11 @@ -160,4 +160,20 @@ - \ No newline at end of file + + + TrueCloudLab + https://git.frostfs.info/api/packages/TrueCloudLab/maven + + + + + TrueCloudLab + https://git.frostfs.info/api/packages/TrueCloudLab/maven + + + TrueCloudLab + https://git.frostfs.info/api/packages/TrueCloudLab/maven + + + From c2d70c4f3e9bd417fffdd827100c9350dffc0caf Mon Sep 17 00:00:00 2001 From: Vitaliy Potyarkin Date: Mon, 10 Mar 2025 14:30:39 +0300 Subject: [PATCH 36/38] [#49] ci: Publish SDK packages to our Maven registry Signed-off-by: Vitaliy Potyarkin --- .forgejo/workflows/publish.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .forgejo/workflows/publish.yml diff --git a/.forgejo/workflows/publish.yml b/.forgejo/workflows/publish.yml new file mode 100644 index 0000000..7c067ca --- /dev/null +++ b/.forgejo/workflows/publish.yml @@ -0,0 +1,22 @@ +on: + push: + workflow_dispatch: + +jobs: + image: + name: Publish Maven packages + runs-on: docker + container: git.frostfs.info/truecloudlab/env:openjdk-11-maven-3.8.6 + steps: + - name: Clone git repo + uses: actions/checkout@v3 + + - name: Publish release packages + run: mvn clean --batch-mode --update-snapshots deploy + if: >- + startsWith(github.ref, 'refs/tags/v') && + (github.event_name == 'workflow_dispatch' || github.event_name == 'push') + env: + MAVEN_REGISTRY: TrueCloudLab + MAVEN_REGISTRY_USER: ${{secrets.MAVEN_REGISTRY_USER}} + MAVEN_REGISTRY_PASSWORD: ${{secrets.MAVEN_REGISTRY_PASSWORD}} From 6546e98cf6a32859ce72904f6d0cab4bb0dbdffb Mon Sep 17 00:00:00 2001 From: Ori Bruk Date: Tue, 22 Apr 2025 17:54:10 +0300 Subject: [PATCH 37/38] [#52] Add placement vector Signed-off-by: Ori Bruk --- checkstyle.xml | 2 +- .../frostfs/sdk/constants/CryptoConst.java | 4 + .../info/frostfs/sdk/placement/Context.java | 369 ++++++++++++++++++ .../frostfs/sdk/placement/HasherList.java | 20 + .../info/frostfs/sdk/placement/MeanAgg.java | 18 + .../frostfs/sdk/placement/MeanIQRAgg.java | 57 +++ .../info/frostfs/sdk/placement/MinAgg.java | 24 ++ .../sdk/placement/NodeAttributePair.java | 15 + .../frostfs/sdk/placement/Normalizer.java | 5 + .../sdk/placement/PlacementVector.java | 197 ++++++++++ .../frostfs/sdk/placement/ReverseMinNorm.java | 14 + .../sdk/placement/SelectFilterExpr.java | 16 + .../frostfs/sdk/placement/SigmoidNorm.java | 19 + .../info/frostfs/sdk/placement/Tools.java | 123 ++++++ .../sdk/placement/PlacementVectorTest.java | 238 +++++++++++ .../test/resources/placement/cbf_default.yml | 48 +++ .../test/resources/placement/cbf_minimal.yml | 52 +++ .../resources/placement/cbf_requirements.yml | 82 ++++ .../resources/placement/filter_complex.yml | 207 ++++++++++ .../placement/filter_invalid_integer.yml | 43 ++ .../resources/placement/filter_simple.yml | 224 +++++++++++ .../src/test/resources/placement/hrw_sort.yml | 118 ++++++ .../src/test/resources/placement/issue213.yml | 52 +++ .../test/resources/placement/many_selects.yml | 141 +++++++ .../test/resources/placement/multiple_rep.yml | 46 +++ .../placement/multiple_rep_asymmetric.yml | 162 ++++++++ .../test/resources/placement/non_strict.yml | 52 +++ .../src/test/resources/placement/rep_only.yml | 62 +++ .../placement/select_no_attribute.yml | 56 +++ .../resources/placement/selector_invalid.yml | 47 +++ .../frostfs/sdk/constants/ErrorConst.java | 12 + .../info/frostfs/sdk/constants/AppConst.java | 4 + .../frostfs/sdk/constants/AttributeConst.java | 11 + .../info/frostfs/sdk/dto/netmap/Filter.java | 2 +- .../info/frostfs/sdk/dto/netmap/Hasher.java | 5 + .../info/frostfs/sdk/dto/netmap/NodeInfo.java | 37 +- .../info/frostfs/sdk/dto/netmap/Replica.java | 13 +- .../info/frostfs/sdk/dto/netmap/Selector.java | 12 +- .../enums/{ => netmap}/FilterOperation.java | 2 +- .../sdk/enums/{ => netmap}/NodeState.java | 2 +- .../enums/{ => netmap}/SelectorClause.java | 2 +- .../sdk/mappers/netmap/FilterMapper.java | 2 +- .../sdk/mappers/netmap/NodeInfoMapper.java | 2 +- .../sdk/mappers/netmap/ReplicaMapper.java | 15 +- .../sdk/mappers/netmap/SelectorMapper.java | 2 +- .../sdk/mappers/netmap/FilterMapperTest.java | 2 +- .../mappers/netmap/NodeInfoMapperTest.java | 2 +- .../mappers/netmap/SelectorMapperTest.java | 2 +- pom.xml | 12 +- 49 files changed, 2630 insertions(+), 24 deletions(-) create mode 100644 client/src/main/java/info/frostfs/sdk/placement/Context.java create mode 100644 client/src/main/java/info/frostfs/sdk/placement/HasherList.java create mode 100644 client/src/main/java/info/frostfs/sdk/placement/MeanAgg.java create mode 100644 client/src/main/java/info/frostfs/sdk/placement/MeanIQRAgg.java create mode 100644 client/src/main/java/info/frostfs/sdk/placement/MinAgg.java create mode 100644 client/src/main/java/info/frostfs/sdk/placement/NodeAttributePair.java create mode 100644 client/src/main/java/info/frostfs/sdk/placement/Normalizer.java create mode 100644 client/src/main/java/info/frostfs/sdk/placement/PlacementVector.java create mode 100644 client/src/main/java/info/frostfs/sdk/placement/ReverseMinNorm.java create mode 100644 client/src/main/java/info/frostfs/sdk/placement/SelectFilterExpr.java create mode 100644 client/src/main/java/info/frostfs/sdk/placement/SigmoidNorm.java create mode 100644 client/src/main/java/info/frostfs/sdk/placement/Tools.java create mode 100644 client/src/test/java/info/frostfs/sdk/placement/PlacementVectorTest.java create mode 100644 client/src/test/resources/placement/cbf_default.yml create mode 100644 client/src/test/resources/placement/cbf_minimal.yml create mode 100644 client/src/test/resources/placement/cbf_requirements.yml create mode 100644 client/src/test/resources/placement/filter_complex.yml create mode 100644 client/src/test/resources/placement/filter_invalid_integer.yml create mode 100644 client/src/test/resources/placement/filter_simple.yml create mode 100644 client/src/test/resources/placement/hrw_sort.yml create mode 100644 client/src/test/resources/placement/issue213.yml create mode 100644 client/src/test/resources/placement/many_selects.yml create mode 100644 client/src/test/resources/placement/multiple_rep.yml create mode 100644 client/src/test/resources/placement/multiple_rep_asymmetric.yml create mode 100644 client/src/test/resources/placement/non_strict.yml create mode 100644 client/src/test/resources/placement/rep_only.yml create mode 100644 client/src/test/resources/placement/select_no_attribute.yml create mode 100644 client/src/test/resources/placement/selector_invalid.yml create mode 100644 models/src/main/java/info/frostfs/sdk/dto/netmap/Hasher.java rename models/src/main/java/info/frostfs/sdk/enums/{ => netmap}/FilterOperation.java (95%) rename models/src/main/java/info/frostfs/sdk/enums/{ => netmap}/NodeState.java (94%) rename models/src/main/java/info/frostfs/sdk/enums/{ => netmap}/SelectorClause.java (94%) diff --git a/checkstyle.xml b/checkstyle.xml index ebe929e..1bb24e7 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -76,7 +76,7 @@ - + diff --git a/client/src/main/java/info/frostfs/sdk/constants/CryptoConst.java b/client/src/main/java/info/frostfs/sdk/constants/CryptoConst.java index 8865173..fd8ef2c 100644 --- a/client/src/main/java/info/frostfs/sdk/constants/CryptoConst.java +++ b/client/src/main/java/info/frostfs/sdk/constants/CryptoConst.java @@ -5,6 +5,10 @@ public class CryptoConst { public static final int RFC6979_SIGNATURE_SIZE = 64; public static final int HASH_SIGNATURE_SIZE = 65; + public static final int MURMUR_MULTIPLIER = 33; + public static final long LANDAU_PRIME_DIVISOR_64BIT = 0xc4ceb9fe1a85ec53L; + public static final long LANDAU_PRIME_DIVISOR_65BIT = 0xff51afd7ed558ccdL; + private CryptoConst() { } } diff --git a/client/src/main/java/info/frostfs/sdk/placement/Context.java b/client/src/main/java/info/frostfs/sdk/placement/Context.java new file mode 100644 index 0000000..64fcb28 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/placement/Context.java @@ -0,0 +1,369 @@ +package info.frostfs.sdk.placement; + +import info.frostfs.sdk.dto.netmap.*; +import info.frostfs.sdk.enums.netmap.FilterOperation; +import info.frostfs.sdk.enums.netmap.SelectorClause; +import info.frostfs.sdk.exceptions.FrostFSException; +import lombok.Getter; +import lombok.Setter; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; + +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static info.frostfs.sdk.constants.AttributeConst.ATTRIBUTE_CAPACITY; +import static info.frostfs.sdk.constants.AttributeConst.ATTRIBUTE_PRICE; +import static info.frostfs.sdk.constants.ErrorConst.*; + +@Getter +@Setter +public final class Context { + public static final String MAIN_FILTER_NAME = "*"; + public static final String LIKE_WILDCARD = "*"; + + // network map to operate on + private final NetmapSnapshot netMap; + + // cache of processed filters + private final Map processedFilters = new HashMap<>(); + + // cache of processed selectors + private final Map processedSelectors = new HashMap<>(); + + // stores results of selector processing + private final Map>> selections = new HashMap<>(); + + // cache of parsed numeric values + private final Map numCache = new HashMap<>(); + private final Map usedNodes = new HashMap<>(); + private final Function weightFunc; + private byte[] hrwSeed; + private long hrwSeedHash; + private int cbf; + private boolean strict; + + public Context(NetmapSnapshot netMap) { + this.netMap = netMap; + this.weightFunc = Tools.defaultWeightFunc(netMap.getNodeInfoCollection()); + } + + private static Pair calcNodesCount(Selector selector) { + return selector.getClause() == SelectorClause.SAME + ? new ImmutablePair<>(1, selector.getCount()) + : new ImmutablePair<>(selector.getCount(), 1); + } + + private static double calcBucketWeight(List ns, MeanIQRAgg a, Function wf) { + for (NodeInfo node : ns) { + a.add(wf.apply(node)); + } + return a.compute(); + } + + public void processFilters(PlacementPolicy policy) { + for (Filter filter : policy.getFilters()) { + processFilter(filter, true); + } + } + + private void processFilter(Filter filter, boolean top) { + String filterName = filter.getName(); + if (MAIN_FILTER_NAME.equals(filterName)) { + throw new FrostFSException(String.format(INVALID_FILTER_NAME_TEMPLATE, MAIN_FILTER_NAME)); + } + + if (top && (filterName == null || filterName.isEmpty())) { + throw new FrostFSException(UNNAMED_TOP_FILTER); + } + + if (!top && filterName != null && !filterName.isEmpty() && !processedFilters.containsKey(filterName)) { + throw new FrostFSException(FILTER_NOT_FOUND); + } + + if (filter.getOperation() == FilterOperation.AND || + filter.getOperation() == FilterOperation.OR || + filter.getOperation() == FilterOperation.NOT) { + + for (Filter f : filter.getFilters()) { + processFilter(f, false); + } + } else { + if (filter.getFilters().length != 0) { + throw new FrostFSException(NON_EMPTY_FILTERS); + } else if (!top && filterName != null && !filterName.isEmpty()) { + // named reference + return; + } + + switch (filter.getOperation()) { + case EQ: + case NE: + case LIKE: + break; + case GT: + case GE: + case LT: + case LE: + long n = Long.parseLong(filter.getValue()); + numCache.put(filter.getValue(), n); + break; + default: + throw new FrostFSException(String.format(INVALID_FILTER_OPERATION_TEMPLATE, filter.getOperation())); + } + } + + if (top) { + processedFilters.put(filterName, filter); + } + } + + public void processSelectors(PlacementPolicy policy) { + for (Selector selector : policy.getSelectors()) { + String filterName = selector.getFilter(); + if (!MAIN_FILTER_NAME.equals(filterName)) { + if (selector.getFilter() == null || !processedFilters.containsKey(selector.getFilter())) { + throw new FrostFSException(String.format(FILTER_NOT_FOUND_TEMPLATE, filterName)); + } + } + + processedSelectors.put(selector.getName(), selector); + List> selection = getSelection(selector); + selections.put(selector.getName(), selection); + } + } + + private NodeAttributePair[] getSelectionBase(Selector selector) { + String fName = selector.getFilter(); + if (fName == null) { + throw new FrostFSException(FILTER_NAME_IS_EMPTY); + } + + Filter f = processedFilters.get(fName); + boolean isMain = MAIN_FILTER_NAME.equals(fName); + List result = new ArrayList<>(); + + Map> nodeMap = new HashMap<>(); + String attr = selector.getAttribute(); + + for (NodeInfo node : netMap.getNodeInfoCollection()) { + if (usedNodes.containsKey(node.getHash())) { + continue; + } + + if (isMain || match(f, node)) { + if (attr == null) { + result.add(new NodeAttributePair("", new NodeInfo[]{node})); + } else { + String v = node.getAttributes().get(attr); + List nodes = nodeMap.computeIfAbsent(v, k -> new ArrayList<>()); + nodes.add(node); + } + } + } + + if (attr != null && !attr.isEmpty()) { + for (Map.Entry> entry : nodeMap.entrySet()) { + result.add(new NodeAttributePair(entry.getKey(), entry.getValue().toArray(NodeInfo[]::new))); + } + } + + if (hrwSeed != null && hrwSeed.length != 0) { + NodeAttributePair[] sortedNodes = new NodeAttributePair[result.size()]; + + for (int i = 0; i < result.size(); i++) { + double[] ws = new double[result.get(i).getNodes().length]; + NodeAttributePair res = result.get(i); + Tools.appendWeightsTo(res.getNodes(), weightFunc, ws); + sortedNodes[i] = new NodeAttributePair( + res.getAttr(), + Tools.sortHasherSliceByWeightValue(Arrays.asList(res.getNodes()), ws, hrwSeedHash) + .toArray(NodeInfo[]::new) + ); + } + + return sortedNodes; + } + return result.toArray(new NodeAttributePair[0]); + } + + public List> getSelection(Selector s) { + Pair counts = calcNodesCount(s); + int bucketCount = counts.getKey(); + int nodesInBucket = counts.getValue(); + + NodeAttributePair[] buckets = getSelectionBase(s); + + if (strict && buckets.length < bucketCount) { + throw new FrostFSException(String.format(NOT_ENOUGH_NODES_TEMPLATE, s.getName())); + } + + if (hrwSeed == null || hrwSeed.length == 0) { + if (s.getAttribute() == null || s.getAttribute().isEmpty()) { + Arrays.sort(buckets, Comparator.comparing(b -> b.getNodes()[0].getHash())); + } else { + Arrays.sort(buckets, Comparator.comparing(NodeAttributePair::getAttr)); + } + } + + int maxNodesInBucket = nodesInBucket * cbf; + + List> res = new ArrayList<>(buckets.length); + List> fallback = new ArrayList<>(buckets.length); + + for (NodeAttributePair bucket : buckets) { + List ns = Arrays.asList(bucket.getNodes()); + if (ns.size() >= maxNodesInBucket) { + res.add(new ArrayList<>(ns.subList(0, maxNodesInBucket))); + } else if (ns.size() >= nodesInBucket) { + fallback.add(new ArrayList<>(ns)); + } + } + + if (res.size() < bucketCount) { + res.addAll(fallback); + + if (strict && res.size() < bucketCount) { + throw new FrostFSException(String.format(NOT_ENOUGH_NODES_TEMPLATE, s.getName())); + } + } + + if (hrwSeed != null && hrwSeed.length != 0) { + double[] weights = new double[res.size()]; + var a = new MeanIQRAgg(); + + for (int i = 0; i < res.size(); i++) { + a.clear(); + weights[i] = calcBucketWeight(res.get(i), a, weightFunc); + } + + List hashers = res.stream() + .map(HasherList::new) + .collect(Collectors.toList()); + + hashers = Tools.sortHasherSliceByWeightValue(hashers, weights, hrwSeedHash); + + for (int i = 0; i < res.size(); i++) { + res.set(i, hashers.get(i).getNodes()); + } + } + + if (res.size() < bucketCount) { + if (strict && res.isEmpty()) { + throw new FrostFSException(NOT_ENOUGH_NODES); + } + bucketCount = res.size(); + } + + if (s.getAttribute() == null || s.getAttribute().isEmpty()) { + fallback = res.subList(bucketCount, res.size()); + res = new ArrayList<>(res.subList(0, bucketCount)); + + for (int i = 0; i < fallback.size(); i++) { + int index = i % bucketCount; + if (res.get(index).size() >= maxNodesInBucket) { + break; + } + res.get(index).addAll(fallback.get(i)); + } + } + + return res.subList(0, bucketCount); + } + + private boolean matchKeyValue(Filter f, NodeInfo nodeInfo) { + switch (f.getOperation()) { + case EQ: + return nodeInfo.getAttributes().containsKey(f.getKey()) && + nodeInfo.getAttributes().get(f.getKey()).equals(f.getValue()); + case LIKE: + boolean hasPrefix = f.getValue().startsWith(LIKE_WILDCARD); + boolean hasSuffix = f.getValue().endsWith(LIKE_WILDCARD); + + int start = hasPrefix ? LIKE_WILDCARD.length() : 0; + int end = hasSuffix ? f.getValue().length() - LIKE_WILDCARD.length() : f.getValue().length(); + String str = f.getValue().substring(start, end); + + if (hasPrefix && hasSuffix) { + return nodeInfo.getAttributes().get(f.getKey()).contains(str); + } + if (hasPrefix) { + return nodeInfo.getAttributes().get(f.getKey()).endsWith(str); + } + if (hasSuffix) { + return nodeInfo.getAttributes().get(f.getKey()).startsWith(str); + } + return nodeInfo.getAttributes().get(f.getKey()).equals(f.getValue()); + case NE: + return !nodeInfo.getAttributes().get(f.getKey()).equals(f.getValue()); + default: + long attr; + switch (f.getKey()) { + case ATTRIBUTE_PRICE: + attr = nodeInfo.getPrice().longValue(); + break; + case ATTRIBUTE_CAPACITY: + attr = nodeInfo.getCapacity().longValue(); + break; + default: + try { + attr = Long.parseLong(nodeInfo.getAttributes().get(f.getKey())); + } catch (NumberFormatException e) { + return false; + } + break; + } + + switch (f.getOperation()) { + case GT: + return attr > numCache.get(f.getValue()); + case GE: + return attr >= numCache.get(f.getValue()); + case LT: + return attr < numCache.get(f.getValue()); + case LE: + return attr <= numCache.get(f.getValue()); + default: + break; + } + break; + } + return false; + } + + boolean match(Filter f, NodeInfo nodeInfo) { + if (f == null) { + return false; + } + + switch (f.getOperation()) { + case NOT: + Filter[] inner = f.getFilters(); + Filter fSub = inner[0]; + + if (inner[0].getName() != null && !inner[0].getName().isEmpty()) { + fSub = processedFilters.get(inner[0].getName()); + } + return !match(fSub, nodeInfo); + case AND: + case OR: + for (int i = 0; i < f.getFilters().length; i++) { + Filter currentFilter = f.getFilters()[i]; + + if (currentFilter.getName() != null && !currentFilter.getName().isEmpty()) { + currentFilter = processedFilters.get(currentFilter.getName()); + } + + boolean ok = match(currentFilter, nodeInfo); + + if (ok == (f.getOperation() == FilterOperation.OR)) { + return ok; + } + } + return f.getOperation() == FilterOperation.AND; + default: + return matchKeyValue(f, nodeInfo); + } + } +} diff --git a/client/src/main/java/info/frostfs/sdk/placement/HasherList.java b/client/src/main/java/info/frostfs/sdk/placement/HasherList.java new file mode 100644 index 0000000..ee5a7ce --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/placement/HasherList.java @@ -0,0 +1,20 @@ +package info.frostfs.sdk.placement; + +import info.frostfs.sdk.dto.netmap.Hasher; +import info.frostfs.sdk.dto.netmap.NodeInfo; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.apache.commons.collections4.CollectionUtils; + +import java.util.List; + +@Getter +@AllArgsConstructor +public final class HasherList implements Hasher { + private final List nodes; + + @Override + public long getHash() { + return CollectionUtils.isNotEmpty(nodes) ? nodes.get(0).getHash() : 0L; + } +} diff --git a/client/src/main/java/info/frostfs/sdk/placement/MeanAgg.java b/client/src/main/java/info/frostfs/sdk/placement/MeanAgg.java new file mode 100644 index 0000000..d3464d4 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/placement/MeanAgg.java @@ -0,0 +1,18 @@ +package info.frostfs.sdk.placement; + +import java.math.BigInteger; + +public class MeanAgg { + private double mean; + private int count; + + public void add(BigInteger n) { + int c = count + 1; + mean = mean * count / c + n.doubleValue() / c; + count++; + } + + public double compute() { + return mean; + } +} diff --git a/client/src/main/java/info/frostfs/sdk/placement/MeanIQRAgg.java b/client/src/main/java/info/frostfs/sdk/placement/MeanIQRAgg.java new file mode 100644 index 0000000..b61b25e --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/placement/MeanIQRAgg.java @@ -0,0 +1,57 @@ +package info.frostfs.sdk.placement; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public final class MeanIQRAgg { + private static final int MIN_LN = 4; + + private final List arr = new ArrayList<>(); + + public MeanIQRAgg() { + } + + public void add(double d) { + arr.add(d); + } + + public double compute() { + int length = arr.size(); + if (length == 0) { + return 0; + } + + List sorted = new ArrayList<>(arr); + Collections.sort(sorted); + + double minV, maxV; + + if (length < MIN_LN) { + minV = sorted.get(0); + maxV = sorted.get(length - 1); + } else { + int start = length / MIN_LN; + int end = length * 3 / MIN_LN - 1; + + minV = sorted.get(start); + maxV = sorted.get(end); + } + + int count = 0; + double sum = 0; + + for (var e : sorted) { + if (e >= minV && e <= maxV) { + sum += e; + count++; + } + } + + return count == 0 ? 0 : sum / count; + } + + public void clear() { + arr.clear(); + } +} diff --git a/client/src/main/java/info/frostfs/sdk/placement/MinAgg.java b/client/src/main/java/info/frostfs/sdk/placement/MinAgg.java new file mode 100644 index 0000000..e2855b5 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/placement/MinAgg.java @@ -0,0 +1,24 @@ +package info.frostfs.sdk.placement; + +import java.math.BigInteger; + +public class MinAgg { + private double min; + private boolean minFound; + + public void add(BigInteger n) { + if (!minFound) { + min = n.doubleValue(); + minFound = true; + return; + } + + if (n.doubleValue() < min) { + min = n.doubleValue(); + } + } + + public double compute() { + return min; + } +} diff --git a/client/src/main/java/info/frostfs/sdk/placement/NodeAttributePair.java b/client/src/main/java/info/frostfs/sdk/placement/NodeAttributePair.java new file mode 100644 index 0000000..9fd1660 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/placement/NodeAttributePair.java @@ -0,0 +1,15 @@ +package info.frostfs.sdk.placement; + +import info.frostfs.sdk.dto.netmap.NodeInfo; +import lombok.Getter; + +@Getter +public class NodeAttributePair { + private final String attr; + private final NodeInfo[] nodes; + + NodeAttributePair(String attr, NodeInfo[] nodes) { + this.attr = attr; + this.nodes = nodes; + } +} diff --git a/client/src/main/java/info/frostfs/sdk/placement/Normalizer.java b/client/src/main/java/info/frostfs/sdk/placement/Normalizer.java new file mode 100644 index 0000000..587150d --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/placement/Normalizer.java @@ -0,0 +1,5 @@ +package info.frostfs.sdk.placement; + +public interface Normalizer { + double normalize(double w); +} diff --git a/client/src/main/java/info/frostfs/sdk/placement/PlacementVector.java b/client/src/main/java/info/frostfs/sdk/placement/PlacementVector.java new file mode 100644 index 0000000..f20c75f --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/placement/PlacementVector.java @@ -0,0 +1,197 @@ +package info.frostfs.sdk.placement; + +import info.frostfs.sdk.dto.netmap.*; +import info.frostfs.sdk.exceptions.FrostFSException; +import lombok.AllArgsConstructor; +import org.apache.commons.codec.digest.MurmurHash3; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.function.Function; + +import static info.frostfs.sdk.constants.ErrorConst.SELECTOR_NOT_FOUND_TEMPLATE; +import static info.frostfs.sdk.constants.ErrorConst.VECTORS_IS_NULL; + +@AllArgsConstructor +public final class PlacementVector { + private final NetmapSnapshot netmapSnapshot; + + private static NodeInfo[] flattenNodes(List> nodes) { + int size = nodes.stream().mapToInt(List::size).sum(); + NodeInfo[] result = new NodeInfo[size]; + + int i = 0; + for (List ns : nodes) { + for (NodeInfo n : ns) { + result[i++] = n; + } + } + return result; + } + + /* + * PlacementVectors sorts container nodes returned by ContainerNodes method + * and returns placement vectors for the entity identified by the given pivot. + * For example, in order to build node list to store the object, + * binary-encoded object identifier can be used as pivot. + * Result is deterministic for the fixed NetMap and parameters. + * */ + public NodeInfo[][] placementVectors(NodeInfo[][] vectors, byte[] pivot) { + if (vectors == null) { + throw new FrostFSException(VECTORS_IS_NULL); + } + + long hash = MurmurHash3.hash128x64(pivot, 0, pivot.length, 0)[0]; + + Function wf = Tools.defaultWeightFunc(netmapSnapshot.getNodeInfoCollection()); + + NodeInfo[][] result = new NodeInfo[vectors.length][]; + int maxSize = Arrays.stream(vectors) + .mapToInt(v -> v.length) + .max() + .orElse(0); + + double[] spanWeights = new double[maxSize]; + + for (int i = 0; i < vectors.length; i++) { + result[i] = Arrays.copyOf(vectors[i], vectors[i].length); + + Tools.appendWeightsTo(result[i], wf, spanWeights); + + List sorted = Tools.sortHasherSliceByWeightValue( + Arrays.asList(result[i]), + spanWeights, + hash + ); + result[i] = sorted.toArray(new NodeInfo[0]); + } + + return result; + } + + /* + * SelectFilterNodes returns a two-dimensional list of nodes as a result of applying the given + * SelectFilterExpr to the NetMap. If the SelectFilterExpr contains only filters, the result contains + * a single row with the result of the last filter application. If the SelectFilterExpr contains only selectors, + * the result contains the selection rows of the last select application. + * */ + public List> selectFilterNodes(SelectFilterExpr expr) { + PlacementPolicy policy = new PlacementPolicy( + null, + false, + expr.getCbf(), + expr.getFilters().toArray(Filter[]::new), + new Selector[]{expr.getSelector()} + ); + + Context ctx = new Context(netmapSnapshot); + ctx.setCbf(expr.getCbf()); + + ctx.processFilters(policy); + ctx.processSelectors(policy); + + List> ret = new ArrayList<>(); + + if (expr.getSelector() == null) { + Filter lastFilter = expr.getFilters().get(expr.getFilters().size() - 1); + List subCollection = new ArrayList<>(); + ret.add(subCollection); + + for (NodeInfo nodeInfo : netmapSnapshot.getNodeInfoCollection()) { + if (ctx.match(ctx.getProcessedFilters().get(lastFilter.getName()), nodeInfo)) { + subCollection.add(nodeInfo); + } + } + } else if (expr.getSelector().getName() != null) { + List> sel = ctx.getSelection( + ctx.getProcessedSelectors().get(expr.getSelector().getName()) + ); + + for (List ns : sel) { + List subCollection = new ArrayList<>(ns); + ret.add(subCollection); + } + } + + return ret; + } + + /* + * ContainerNodes returns two-dimensional list of nodes as a result of applying given PlacementPolicy to the NetMap. + * Each line of the list corresponds to a replica descriptor. + * Line order corresponds to order of ReplicaDescriptor list in the policy. + * Nodes are pre-filtered according to the Filter list from the policy, and then selected by Selector list. + * Result is deterministic for the fixed NetMap and parameters. + * + * Result can be used in PlacementVectors. + * */ + public NodeInfo[][] containerNodes(PlacementPolicy p, byte[] pivot) { + Context c = new Context(netmapSnapshot); + c.setCbf(p.getBackupFactory() == 0 ? 3 : p.getBackupFactory()); + + if (pivot != null && pivot.length > 0) { + c.setHrwSeed(pivot); + + var hash = MurmurHash3.hash128x64(pivot, 0, pivot.length, 0)[0]; + c.setHrwSeedHash(hash); + } + + c.processFilters(p); + c.processSelectors(p); + + boolean unique = p.isUnique(); + + List> result = new ArrayList<>(p.getReplicas().length); + for (int i = 0; i < p.getReplicas().length; i++) { + result.add(new ArrayList<>()); + } + + for (int i = 0; i < p.getReplicas().length; i++) { + String sName = p.getReplicas()[i].getSelector(); + + if ((sName == null || sName.isEmpty()) && + !(p.getReplicas().length == 1 && p.getSelectors().length == 1)) { + + Selector s = new Selector( + "", p.getReplicas()[i].getCountNodes(), null, null, + Context.MAIN_FILTER_NAME + ); + + List> nodes = c.getSelection(s); + result.get(i).addAll(Arrays.asList(flattenNodes(nodes))); + + if (unique) { + for (NodeInfo n : result.get(i)) { + c.getUsedNodes().put(n.getHash(), true); + } + } + continue; + } + + if (unique) { + Selector s = c.getProcessedSelectors().get(sName); + if (s == null) { + throw new FrostFSException(String.format(SELECTOR_NOT_FOUND_TEMPLATE, sName)); + } + + List> nodes = c.getSelection(s); + result.get(i).addAll(Arrays.asList(flattenNodes(nodes))); + + for (NodeInfo n : result.get(i)) { + c.getUsedNodes().put(n.getHash(), true); + } + } else { + List> nodes = c.getSelections().get(sName); + result.get(i).addAll(Arrays.asList(flattenNodes(nodes))); + } + } + + NodeInfo[][] collection = new NodeInfo[result.size()][]; + for (int i = 0; i < result.size(); i++) { + collection[i] = result.get(i).toArray(new NodeInfo[0]); + } + + return collection; + } +} diff --git a/client/src/main/java/info/frostfs/sdk/placement/ReverseMinNorm.java b/client/src/main/java/info/frostfs/sdk/placement/ReverseMinNorm.java new file mode 100644 index 0000000..1bcc03a --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/placement/ReverseMinNorm.java @@ -0,0 +1,14 @@ +package info.frostfs.sdk.placement; + +public class ReverseMinNorm implements Normalizer { + private final double min; + + public ReverseMinNorm(double min) { + this.min = min; + } + + @Override + public double normalize(double w) { + return (min + 1) / (w + 1); + } +} diff --git a/client/src/main/java/info/frostfs/sdk/placement/SelectFilterExpr.java b/client/src/main/java/info/frostfs/sdk/placement/SelectFilterExpr.java new file mode 100644 index 0000000..b7dd898 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/placement/SelectFilterExpr.java @@ -0,0 +1,16 @@ +package info.frostfs.sdk.placement; + +import info.frostfs.sdk.dto.netmap.Filter; +import info.frostfs.sdk.dto.netmap.Selector; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.List; + +@Getter +@AllArgsConstructor +public class SelectFilterExpr { + private final int cbf; + private final Selector selector; + private final List filters; +} diff --git a/client/src/main/java/info/frostfs/sdk/placement/SigmoidNorm.java b/client/src/main/java/info/frostfs/sdk/placement/SigmoidNorm.java new file mode 100644 index 0000000..c0e867a --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/placement/SigmoidNorm.java @@ -0,0 +1,19 @@ +package info.frostfs.sdk.placement; + +public class SigmoidNorm implements Normalizer { + private final double scale; + + public SigmoidNorm(double scale) { + this.scale = scale; + } + + @Override + public double normalize(double w) { + if (scale == 0) { + return 0; + } + + double x = w / scale; + return x / (1 + x); + } +} diff --git a/client/src/main/java/info/frostfs/sdk/placement/Tools.java b/client/src/main/java/info/frostfs/sdk/placement/Tools.java new file mode 100644 index 0000000..0140078 --- /dev/null +++ b/client/src/main/java/info/frostfs/sdk/placement/Tools.java @@ -0,0 +1,123 @@ +package info.frostfs.sdk.placement; + +import info.frostfs.sdk.dto.netmap.Hasher; +import info.frostfs.sdk.dto.netmap.NodeInfo; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.function.Function; + +import static info.frostfs.sdk.constants.AppConst.UNSIGNED_LONG_MASK; +import static info.frostfs.sdk.constants.CryptoConst.*; + +public final class Tools { + private Tools() { + } + + public static long distance(long x, long y) { + long acc = x ^ y; + acc ^= acc >>> MURMUR_MULTIPLIER; + acc *= LANDAU_PRIME_DIVISOR_65BIT; + acc ^= acc >>> MURMUR_MULTIPLIER; + acc *= LANDAU_PRIME_DIVISOR_64BIT; + acc ^= acc >>> MURMUR_MULTIPLIER; + return acc; + } + + public static void appendWeightsTo(NodeInfo[] nodes, Function wf, double[] weights) { + if (weights.length < nodes.length) { + weights = new double[nodes.length]; + } + for (int i = 0; i < nodes.length; i++) { + weights[i] = wf.apply(nodes[i]); + } + } + + public static List sortHasherSliceByWeightValue(List nodes, double[] weights, long hash) { + if (nodes.isEmpty()) { + return nodes; + } + + boolean allEquals = true; + if (weights.length > 1) { + for (int i = 1; i < weights.length; i++) { + if (weights[i] != weights[0]) { + allEquals = false; + break; + } + } + } + + Double[] dist = new Double[nodes.size()]; + + if (allEquals) { + for (int i = 0; i < dist.length; i++) { + long x = nodes.get(i).getHash(); + dist[i] = toUnsignedBigInteger(distance(x, hash)).doubleValue(); + } + return sortHasherByDistance(nodes, dist, true); + } + + for (int i = 0; i < dist.length; i++) { + var reverse = UNSIGNED_LONG_MASK.subtract(toUnsignedBigInteger(distance(nodes.get(i).getHash(), hash))); + dist[i] = reverse.doubleValue() * weights[i]; + } + + return sortHasherByDistance(nodes, dist, false); + } + + public static > List sortHasherByDistance( + List nodes, N[] dist, boolean asc + ) { + IndexedValue[] indexes = new IndexedValue[nodes.size()]; + for (int i = 0; i < dist.length; i++) { + indexes[i] = new IndexedValue<>(nodes.get(i), dist[i]); + } + + if (asc) { + Arrays.sort(indexes, Comparator.comparing(iv -> iv.dist)); + } else { + Arrays.sort(indexes, (iv1, iv2) -> iv2.dist.compareTo(iv1.dist)); + } + + List result = new ArrayList<>(); + for (IndexedValue iv : indexes) { + result.add(iv.nodeInfo); + } + return result; + } + + public static Function defaultWeightFunc(List nodes) { + MeanAgg mean = new MeanAgg(); + MinAgg minV = new MinAgg(); + + for (NodeInfo node : nodes) { + mean.add(node.getCapacity()); + minV.add(node.getPrice()); + } + + return newWeightFunc(new SigmoidNorm(mean.compute()), new ReverseMinNorm(minV.compute())); + } + + private static BigInteger toUnsignedBigInteger(long i) { + return i >= 0 ? BigInteger.valueOf(i) : BigInteger.valueOf(i).and(UNSIGNED_LONG_MASK); + } + + private static Function newWeightFunc(Normalizer capNorm, Normalizer priceNorm) { + return nodeInfo -> capNorm.normalize(nodeInfo.getCapacity().doubleValue()) + * priceNorm.normalize(nodeInfo.getPrice().doubleValue()); + } + + private static class IndexedValue { + final T nodeInfo; + final N dist; + + IndexedValue(T nodeInfo, N dist) { + this.nodeInfo = nodeInfo; + this.dist = dist; + } + } +} diff --git a/client/src/test/java/info/frostfs/sdk/placement/PlacementVectorTest.java b/client/src/test/java/info/frostfs/sdk/placement/PlacementVectorTest.java new file mode 100644 index 0000000..8ae8d48 --- /dev/null +++ b/client/src/test/java/info/frostfs/sdk/placement/PlacementVectorTest.java @@ -0,0 +1,238 @@ +package info.frostfs.sdk.placement; + +import info.frostfs.sdk.dto.netmap.*; +import info.frostfs.sdk.enums.netmap.FilterOperation; +import info.frostfs.sdk.enums.netmap.NodeState; +import info.frostfs.sdk.enums.netmap.SelectorClause; +import lombok.Getter; +import lombok.SneakyThrows; +import org.junit.jupiter.api.Test; +import org.yaml.snakeyaml.Yaml; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; + +public class PlacementVectorTest { + private static final Yaml YAML = new Yaml(); + + private static void compareNodes(Map attrs, NodeInfo nodeInfo) { + assertEquals(attrs.size(), nodeInfo.getAttributes().size()); + assertEquals( + attrs.entrySet().stream().sorted(Map.Entry.comparingByKey()).collect(Collectors.toList()), + nodeInfo.getAttributes().entrySet().stream().sorted(Map.Entry.comparingByKey()).collect(Collectors.toList()) + ); + } + + @SneakyThrows + @Test + public void placementTest() { + Path resourceDirYaml = Paths.get(Objects.requireNonNull(getClass().getClassLoader() + .getResource("placement")).toURI()); + + List yamlFiles; + try (Stream paths = Files.walk(resourceDirYaml)) { + yamlFiles = paths.filter(Files::isRegularFile).collect(Collectors.toList()); + } + + Version v = new Version(2, 13); + String[] addresses = {"localhost", "server1"}; + + for (Path file : yamlFiles) { + TestCase testCase = YAML.loadAs(Files.newInputStream(file), TestCase.class); + + assertNotNull(testCase); + assertNotNull(testCase.nodes); + assertTrue(testCase.nodes.length > 0); + + List nodes = Arrays.stream(testCase.nodes) + .map(n -> new NodeInfo( + n.state, + v, + List.of(addresses), + n.attributes != null ? + Arrays.stream(n.attributes) + .collect(Collectors.toMap(KeyValuePair::getKey, KeyValuePair::getValue)) : + Collections.emptyMap(), + n.getPublicKeyBytes() + )) + .collect(Collectors.toList()); + + NetmapSnapshot netmap = new NetmapSnapshot(100L, nodes); + + assertNotNull(testCase.tests); + + for (var entry : testCase.tests.entrySet()) { + var test = entry.getValue(); + PlacementPolicy policy = new PlacementPolicy( + test.policy.replicas != null ? + Arrays.stream(test.policy.replicas) + .map(r -> new Replica(r.count, r.selector)) + .toArray(Replica[]::new) : + new Replica[0], + test.policy.unique, + test.policy.containerBackupFactor, + test.policy.filters != null + ? Arrays.stream(test.policy.filters) + .map(FilterDto::getFilter) + .toArray(Filter[]::new) + : new Filter[]{}, + test.policy.selectors != null + ? Arrays.stream(test.policy.selectors) + .map(SelectorDto::getSelector) + .toArray(Selector[]::new) + : new Selector[]{} + ); + + try { + var vector = new PlacementVector(netmap); + NodeInfo[][] result = vector.containerNodes(policy, test.getPivotBytes()); + + if (test.result == null) { + if (test.error != null && !test.error.isEmpty()) { + fail("Error is expected but has not been thrown"); + } else { + assertNotNull(test.policy.replicas); + assertEquals(result.length, test.policy.replicas.length); + + for (NodeInfo[] nodesArr : result) { + assertEquals(0, nodesArr.length); + } + } + } else { + assertEquals(test.result.length, result.length); + + for (int i = 0; i < test.result.length; i++) { + assertEquals(test.result[i].length, result[i].length); + for (int j = 0; j < test.result[i].length; j++) { + compareNodes(nodes.get(test.result[i][j]).getAttributes(), result[i][j]); + } + } + + if (test.placement != null + && test.placement.result != null + && test.placement.getPivotBytes() != null) { + NodeInfo[][] placementResult = vector.placementVectors( + result, test.placement.getPivotBytes() + ); + + assertEquals(test.placement.result.length, placementResult.length); + + for (int i = 0; i < placementResult.length; i++) { + assertEquals(test.placement.result[i].length, placementResult[i].length); + for (int j = 0; j < placementResult[i].length; j++) { + compareNodes( + nodes.get(test.placement.result[i][j]).getAttributes(), + placementResult[i][j] + ); + } + } + } + } + } catch (Exception ex) { + if (test.error != null && !test.error.isEmpty()) { + assertTrue(ex.getMessage().contains(test.error)); + } else { + throw ex; + } + } + } + } + } + + + public static class TestCase { + public String name; + public String comment; + public Node[] nodes; + public Map tests; + } + + public static class Node { + public KeyValuePair[] attributes; + public String publicKey; + public String[] addresses; + public NodeState state = NodeState.ONLINE; + + public byte[] getPublicKeyBytes() { + return publicKey == null || publicKey.isEmpty() ? new byte[0] : Base64.getDecoder().decode(publicKey); + } + } + + @Getter + public static class KeyValuePair { + public String key; + public String value; + } + + public static class TestData { + public PolicyDto policy; + public String pivot; + public int[][] result; + public String error; + public ResultData placement; + + public byte[] getPivotBytes() { + return pivot == null ? null : Base64.getDecoder().decode(pivot); + } + } + + public static class PolicyDto { + public boolean unique; + public int containerBackupFactor; + public FilterDto[] filters; + public ReplicaDto[] replicas; + public SelectorDto[] selectors; + } + + public static class SelectorDto { + public int count; + public String name; + public SelectorClause clause; + public String attribute; + public String filter; + + public Selector getSelector() { + return new Selector(name != null ? name : "", count, clause, attribute, filter); + } + } + + public static class FilterDto { + public String name; + public String key; + public FilterOperation op; + public String value; + public FilterDto[] filters; + + public Filter getFilter() { + return new Filter( + name != null ? name : "", + key != null ? key : "", + op, + value != null ? value : "", + filters != null + ? Arrays.stream(filters).map(FilterDto::getFilter).toArray(Filter[]::new) + : new Filter[0] + ); + } + } + + public static class ReplicaDto { + public int count; + public String selector; + } + + public static class ResultData { + public String pivot; + public int[][] result; + + public byte[] getPivotBytes() { + return pivot == null ? null : Base64.getDecoder().decode(pivot); + } + } +} diff --git a/client/src/test/resources/placement/cbf_default.yml b/client/src/test/resources/placement/cbf_default.yml new file mode 100644 index 0000000..c43a703 --- /dev/null +++ b/client/src/test/resources/placement/cbf_default.yml @@ -0,0 +1,48 @@ +name: default CBF is 3 +nodes: + - attributes: + - key: Location + value: Europe + - key: Country + value: RU + - key: City + value: St.Petersburg + - attributes: + - key: Location + value: Europe + - key: Country + value: RU + - key: City + value: Moscow + - attributes: + - key: Location + value: Europe + - key: Country + value: DE + - key: City + value: Berlin + - attributes: + - key: Location + value: Europe + - key: Country + value: FR + - key: City + value: Paris +tests: + set default CBF: + policy: + replicas: + - count: 1 + selector: EU + containerBackupFactor: 0 + selectors: + - name: EU + count: 1 + clause: SAME + attribute: Location + filter: '*' + filters: [] + result: + - - 0 + - 1 + - 2 diff --git a/client/src/test/resources/placement/cbf_minimal.yml b/client/src/test/resources/placement/cbf_minimal.yml new file mode 100644 index 0000000..2fe2642 --- /dev/null +++ b/client/src/test/resources/placement/cbf_minimal.yml @@ -0,0 +1,52 @@ +name: Real node count multiplier is in range [1, specified CBF] +nodes: + - attributes: + - key: ID + value: '1' + - key: Country + value: DE + - attributes: + - key: ID + value: '2' + - key: Country + value: DE + - attributes: + - key: ID + value: '3' + - key: Country + value: DE +tests: + select 2, CBF is 2: + policy: + replicas: + - count: 1 + selector: X + containerBackupFactor: 2 + selectors: + - name: X + count: 2 + clause: SAME + attribute: Country + filter: '*' + filters: [] + result: + - - 0 + - 1 + - 2 + select 3, CBF is 2: + policy: + replicas: + - count: 1 + selector: X + containerBackupFactor: 2 + selectors: + - name: X + count: 3 + clause: SAME + attribute: Country + filter: '*' + filters: [] + result: + - - 0 + - 1 + - 2 diff --git a/client/src/test/resources/placement/cbf_requirements.yml b/client/src/test/resources/placement/cbf_requirements.yml new file mode 100644 index 0000000..ccd58d4 --- /dev/null +++ b/client/src/test/resources/placement/cbf_requirements.yml @@ -0,0 +1,82 @@ +name: CBF requirements +nodes: + - attributes: + - key: ID + value: '1' + - key: Attr + value: Same + - attributes: + - key: ID + value: '2' + - key: Attr + value: Same + - attributes: + - key: ID + value: '3' + - key: Attr + value: Same + - attributes: + - key: ID + value: '4' + - key: Attr + value: Same +tests: + default CBF, no selector: + policy: + replicas: + - count: 2 + containerBackupFactor: 0 + selectors: [] + filters: [] + result: + - - 0 + - 2 + - 1 + - 3 + explicit CBF, no selector: + policy: + replicas: + - count: 2 + containerBackupFactor: 3 + selectors: [] + filters: [] + result: + - - 0 + - 2 + - 1 + - 3 + select distinct, weak CBF: + policy: + replicas: + - count: 2 + selector: X + containerBackupFactor: 3 + selectors: + - name: X + count: 2 + clause: DISTINCT + filter: '*' + filters: [] + result: + - - 0 + - 2 + - 1 + - 3 + select same, weak CBF: + policy: + replicas: + - count: 2 + selector: X + containerBackupFactor: 3 + selectors: + - name: X + count: 2 + clause: SAME + attribute: Attr + filter: '*' + filters: [] + result: + - - 0 + - 1 + - 2 + - 3 diff --git a/client/src/test/resources/placement/filter_complex.yml b/client/src/test/resources/placement/filter_complex.yml new file mode 100644 index 0000000..1abc46a --- /dev/null +++ b/client/src/test/resources/placement/filter_complex.yml @@ -0,0 +1,207 @@ +name: compound filter +nodes: + - attributes: + - key: Storage + value: SSD + - key: Rating + value: '10' + - key: IntField + value: '100' + - key: Param + value: Value1 +tests: + good: + policy: + replicas: + - count: 1 + selector: S + containerBackupFactor: 1 + selectors: + - name: S + count: 1 + clause: DISTINCT + filter: Main + filters: + - name: StorageSSD + key: Storage + op: EQ + value: SSD + filters: [] + - name: GoodRating + key: Rating + op: GE + value: '4' + filters: [] + - name: Main + op: AND + filters: + - name: StorageSSD + op: OPERATION_UNSPECIFIED + filters: [] + - name: '' + key: IntField + op: LT + value: '123' + filters: [] + - name: GoodRating + op: OPERATION_UNSPECIFIED + filters: [] + - op: OR + filters: + - key: Param + op: EQ + value: Value1 + filters: [] + - key: Param + op: EQ + value: Value2 + filters: [] + result: + - - 0 + bad storage type: + policy: + replicas: + - count: 1 + selector: S + containerBackupFactor: 1 + selectors: + - name: S + count: 1 + clause: DISTINCT + filter: Main + filters: + - name: StorageSSD + key: Storage + op: EQ + value: HDD + filters: [] + - name: GoodRating + key: Rating + op: GE + value: '4' + filters: [] + - name: Main + op: AND + filters: + - name: StorageSSD + op: OPERATION_UNSPECIFIED + filters: [] + - name: '' + key: IntField + op: LT + value: '123' + filters: [] + - name: GoodRating + op: OPERATION_UNSPECIFIED + filters: [] + - name: '' + op: OR + filters: + - name: '' + key: Param + op: EQ + value: Value1 + filters: [] + - name: '' + key: Param + op: EQ + value: Value2 + filters: [] + bad rating: + policy: + replicas: + - count: 1 + selector: S + containerBackupFactor: 1 + selectors: + - name: S + count: 1 + clause: DISTINCT + filter: Main + filters: + - name: StorageSSD + key: Storage + op: EQ + value: SSD + filters: [] + - name: GoodRating + key: Rating + op: GE + value: '15' + filters: [] + - name: Main + op: AND + filters: + - name: StorageSSD + op: OPERATION_UNSPECIFIED + filters: [] + - name: '' + key: IntField + op: LT + value: '123' + filters: [] + - name: GoodRating + op: OPERATION_UNSPECIFIED + filters: [] + - name: '' + op: OR + filters: + - name: '' + key: Param + op: EQ + value: Value1 + filters: [] + - name: '' + key: Param + op: EQ + value: Value2 + filters: [] + bad param: + policy: + replicas: + - count: 1 + selector: S + containerBackupFactor: 1 + selectors: + - name: S + count: 1 + clause: DISTINCT + filter: Main + filters: + - name: StorageSSD + key: Storage + op: EQ + value: SSD + filters: [] + - name: GoodRating + key: Rating + op: GE + value: '4' + filters: [] + - name: Main + op: AND + filters: + - name: StorageSSD + op: OPERATION_UNSPECIFIED + filters: [] + - name: '' + key: IntField + op: LT + value: '123' + filters: [] + - name: GoodRating + op: OPERATION_UNSPECIFIED + filters: [] + - name: '' + op: OR + filters: + - name: '' + key: Param + op: EQ + value: Value0 + filters: [] + - name: '' + key: Param + op: EQ + value: Value2 + filters: [] diff --git a/client/src/test/resources/placement/filter_invalid_integer.yml b/client/src/test/resources/placement/filter_invalid_integer.yml new file mode 100644 index 0000000..6674246 --- /dev/null +++ b/client/src/test/resources/placement/filter_invalid_integer.yml @@ -0,0 +1,43 @@ +name: invalid integer field +nodes: + - attributes: + - key: IntegerField + value: 'true' + - attributes: + - key: IntegerField + value: str +tests: + empty string is not casted to 0: + policy: + replicas: + - count: 1 + selector: S + containerBackupFactor: 1 + selectors: + - name: S + count: 1 + clause: DISTINCT + filter: Main + filters: + - name: Main + key: IntegerField + op: LE + value: '8' + filters: [] + non-empty string is not casted to a number: + policy: + replicas: + - count: 1 + selector: S + containerBackupFactor: 1 + selectors: + - name: S + count: 1 + clause: DISTINCT + filter: Main + filters: + - name: Main + key: IntegerField + op: GE + value: '0' + filters: [] diff --git a/client/src/test/resources/placement/filter_simple.yml b/client/src/test/resources/placement/filter_simple.yml new file mode 100644 index 0000000..7fdd84a --- /dev/null +++ b/client/src/test/resources/placement/filter_simple.yml @@ -0,0 +1,224 @@ +name: single-op filters +nodes: + - attributes: + - key: Rating + value: '4' + - key: Country + value: Germany +tests: + GE true: + policy: + replicas: + - count: 1 + selector: S + containerBackupFactor: 1 + selectors: + - name: S + count: 1 + clause: DISTINCT + filter: Main + filters: + - name: Main + key: Rating + op: GE + value: '4' + filters: [] + result: + - - 0 + GE false: + policy: + replicas: + - count: 1 + selector: S + containerBackupFactor: 1 + selectors: + - name: S + count: 1 + clause: DISTINCT + filter: Main + filters: + - name: Main + key: Rating + op: GE + value: '5' + filters: [] + GT true: + policy: + replicas: + - count: 1 + selector: S + containerBackupFactor: 1 + selectors: + - name: S + count: 1 + clause: DISTINCT + filter: Main + filters: + - name: Main + key: Rating + op: GT + value: '3' + filters: [] + result: + - - 0 + GT false: + policy: + replicas: + - count: 1 + selector: S + containerBackupFactor: 1 + selectors: + - name: S + count: 1 + clause: DISTINCT + filter: Main + filters: + - name: Main + key: Rating + op: GT + value: '4' + filters: [] + LE true: + policy: + replicas: + - count: 1 + selector: S + containerBackupFactor: 1 + selectors: + - name: S + count: 1 + clause: DISTINCT + filter: Main + filters: + - name: Main + key: Rating + op: LE + value: '4' + filters: [] + result: + - - 0 + LE false: + policy: + replicas: + - count: 1 + selector: S + containerBackupFactor: 1 + selectors: + - name: S + count: 1 + clause: DISTINCT + filter: Main + filters: + - name: Main + key: Rating + op: LE + value: '3' + filters: [] + LT true: + policy: + replicas: + - count: 1 + selector: S + containerBackupFactor: 1 + selectors: + - name: S + count: 1 + clause: DISTINCT + filter: Main + filters: + - name: Main + key: Rating + op: LT + value: '5' + filters: [] + result: + - - 0 + LT false: + policy: + replicas: + - count: 1 + selector: S + containerBackupFactor: 1 + selectors: + - name: S + count: 1 + clause: DISTINCT + filter: Main + filters: + - name: Main + key: Rating + op: LT + value: '4' + filters: [] + EQ true: + policy: + replicas: + - count: 1 + selector: S + containerBackupFactor: 1 + selectors: + - name: S + count: 1 + clause: DISTINCT + filter: Main + filters: + - name: Main + key: Country + op: EQ + value: Germany + filters: [] + result: + - - 0 + EQ false: + policy: + replicas: + - count: 1 + selector: S + containerBackupFactor: 1 + selectors: + - name: S + count: 1 + clause: DISTINCT + filter: Main + filters: + - name: Main + key: Country + op: EQ + value: China + filters: [] + NE true: + policy: + replicas: + - count: 1 + selector: S + containerBackupFactor: 1 + selectors: + - name: S + count: 1 + clause: DISTINCT + filter: Main + filters: + - name: Main + key: Country + op: NE + value: France + filters: [] + result: + - - 0 + NE false: + policy: + replicas: + - count: 1 + selector: S + containerBackupFactor: 1 + selectors: + - name: S + count: 1 + clause: DISTINCT + filter: Main + filters: + - name: Main + key: Country + op: NE + value: Germany + filters: [] diff --git a/client/src/test/resources/placement/hrw_sort.yml b/client/src/test/resources/placement/hrw_sort.yml new file mode 100644 index 0000000..c84f7c9 --- /dev/null +++ b/client/src/test/resources/placement/hrw_sort.yml @@ -0,0 +1,118 @@ +name: HRW ordering +nodes: + - attributes: + - key: Country + value: Germany + - key: Price + value: '2' + - key: Capacity + value: '10000' + - attributes: + - key: Country + value: Germany + - key: Price + value: '4' + - key: Capacity + value: '1' + - attributes: + - key: Country + value: France + - key: Price + value: '3' + - key: Capacity + value: '10' + - attributes: + - key: Country + value: Russia + - key: Price + value: '2' + - key: Capacity + value: '10000' + - attributes: + - key: Country + value: Russia + - key: Price + value: '1' + - key: Capacity + value: '10000' + - attributes: + - key: Country + value: Russia + - key: Capacity + value: '10000' + - attributes: + - key: Country + value: France + - key: Price + value: '100' + - key: Capacity + value: '1' + - attributes: + - key: Country + value: France + - key: Price + value: '7' + - key: Capacity + value: '10000' + - attributes: + - key: Country + value: Russia + - key: Price + value: '2' + - key: Capacity + value: '1' +tests: + select 3 nodes in 3 distinct countries, same placement: + policy: + replicas: + - count: 1 + selector: Main + containerBackupFactor: 1 + selectors: + - name: Main + count: 3 + clause: DISTINCT + attribute: Country + filter: '*' + filters: [] + pivot: Y29udGFpbmVySUQ= + result: + - - 5 + - 0 + - 7 + placement: + pivot: b2JqZWN0SUQ= + result: + - - 5 + - 0 + - 7 + select 6 nodes in 3 distinct countries, different placement: + policy: + replicas: + - count: 1 + selector: Main + containerBackupFactor: 2 + selectors: + - name: Main + count: 3 + clause: DISTINCT + attribute: Country + filter: '*' + filters: [] + pivot: Y29udGFpbmVySUQ= + result: + - - 5 + - 4 + - 0 + - 1 + - 7 + - 2 + placement: + pivot: b2JqZWN0SUQ= + result: + - - 5 + - 4 + - 0 + - 7 + - 2 + - 1 diff --git a/client/src/test/resources/placement/issue213.yml b/client/src/test/resources/placement/issue213.yml new file mode 100644 index 0000000..8e8aea4 --- /dev/null +++ b/client/src/test/resources/placement/issue213.yml @@ -0,0 +1,52 @@ +name: unnamed selector (nspcc-dev/neofs-api-go#213) +nodes: + - attributes: + - key: Location + value: Europe + - key: Country + value: Russia + - key: City + value: Moscow + - attributes: + - key: Location + value: Europe + - key: Country + value: Russia + - key: City + value: Saint-Petersburg + - attributes: + - key: Location + value: Europe + - key: Country + value: Sweden + - key: City + value: Stockholm + - attributes: + - key: Location + value: Europe + - key: Country + value: Finalnd + - key: City + value: Helsinki +tests: + test: + policy: + replicas: + - count: 4 + containerBackupFactor: 1 + selectors: + - name: '' + count: 4 + clause: DISTINCT + filter: LOC_EU + filters: + - name: LOC_EU + key: Location + op: EQ + value: Europe + filters: [] + result: + - - 0 + - 1 + - 2 + - 3 diff --git a/client/src/test/resources/placement/many_selects.yml b/client/src/test/resources/placement/many_selects.yml new file mode 100644 index 0000000..29efd43 --- /dev/null +++ b/client/src/test/resources/placement/many_selects.yml @@ -0,0 +1,141 @@ +name: single-op filters +nodes: + - attributes: + - key: Country + value: Russia + - key: Rating + value: '1' + - key: City + value: SPB + - attributes: + - key: Country + value: Germany + - key: Rating + value: '5' + - key: City + value: Berlin + - attributes: + - key: Country + value: Russia + - key: Rating + value: '6' + - key: City + value: Moscow + - attributes: + - key: Country + value: France + - key: Rating + value: '4' + - key: City + value: Paris + - attributes: + - key: Country + value: France + - key: Rating + value: '1' + - key: City + value: Lyon + - attributes: + - key: Country + value: Russia + - key: Rating + value: '5' + - key: City + value: SPB + - attributes: + - key: Country + value: Russia + - key: Rating + value: '7' + - key: City + value: Moscow + - attributes: + - key: Country + value: Germany + - key: Rating + value: '3' + - key: City + value: Darmstadt + - attributes: + - key: Country + value: Germany + - key: Rating + value: '7' + - key: City + value: Frankfurt + - attributes: + - key: Country + value: Russia + - key: Rating + value: '9' + - key: City + value: SPB + - attributes: + - key: Country + value: Russia + - key: Rating + value: '9' + - key: City + value: SPB +tests: + Select: + policy: + replicas: + - count: 1 + selector: SameRU + - count: 1 + selector: DistinctRU + - count: 1 + selector: Good + - count: 1 + selector: Main + containerBackupFactor: 2 + selectors: + - name: SameRU + count: 2 + clause: SAME + attribute: City + filter: FromRU + - name: DistinctRU + count: 2 + clause: DISTINCT + attribute: City + filter: FromRU + - name: Good + count: 2 + clause: DISTINCT + attribute: Country + filter: Good + - name: Main + count: 3 + clause: DISTINCT + attribute: Country + filter: '*' + filters: + - name: FromRU + key: Country + op: EQ + value: Russia + - name: Good + key: Rating + op: GE + value: '4' + result: + - - 0 + - 5 + - 9 + - 10 + - - 2 + - 6 + - 0 + - 5 + - - 1 + - 8 + - 2 + - 5 + - - 3 + - 4 + - 1 + - 7 + - 0 + - 2 diff --git a/client/src/test/resources/placement/multiple_rep.yml b/client/src/test/resources/placement/multiple_rep.yml new file mode 100644 index 0000000..448214f --- /dev/null +++ b/client/src/test/resources/placement/multiple_rep.yml @@ -0,0 +1,46 @@ +name: multiple replicas (#215) +nodes: + - attributes: + - key: City + value: Saint-Petersburg + - attributes: + - key: City + value: Moscow + - attributes: + - key: City + value: Berlin + - attributes: + - key: City + value: Paris +tests: + test: + policy: + replicas: + - count: 1 + selector: LOC_SPB_PLACE + - count: 1 + selector: LOC_MSK_PLACE + containerBackupFactor: 1 + selectors: + - name: LOC_SPB_PLACE + count: 1 + clause: CLAUSE_UNSPECIFIED + filter: LOC_SPB + - name: LOC_MSK_PLACE + count: 1 + clause: CLAUSE_UNSPECIFIED + filter: LOC_MSK + filters: + - name: LOC_SPB + key: City + op: EQ + value: Saint-Petersburg + filters: [] + - name: LOC_MSK + key: City + op: EQ + value: Moscow + filters: [] + result: + - - 0 + - - 1 diff --git a/client/src/test/resources/placement/multiple_rep_asymmetric.yml b/client/src/test/resources/placement/multiple_rep_asymmetric.yml new file mode 100644 index 0000000..61f8f76 --- /dev/null +++ b/client/src/test/resources/placement/multiple_rep_asymmetric.yml @@ -0,0 +1,162 @@ +name: multiple REP, asymmetric +nodes: + - attributes: + - key: ID + value: '1' + - key: Country + value: RU + - key: City + value: St.Petersburg + - key: SSD + value: '0' + - attributes: + - key: ID + value: '2' + - key: Country + value: RU + - key: City + value: St.Petersburg + - key: SSD + value: '1' + - attributes: + - key: ID + value: '3' + - key: Country + value: RU + - key: City + value: Moscow + - key: SSD + value: '1' + - attributes: + - key: ID + value: '4' + - key: Country + value: RU + - key: City + value: Moscow + - key: SSD + value: '1' + - attributes: + - key: ID + value: '5' + - key: Country + value: RU + - key: City + value: St.Petersburg + - key: SSD + value: '1' + - attributes: + - key: ID + value: '6' + - key: Continent + value: NA + - key: City + value: NewYork + - attributes: + - key: ID + value: '7' + - key: Continent + value: AF + - key: City + value: Cairo + - attributes: + - key: ID + value: '8' + - key: Continent + value: AF + - key: City + value: Cairo + - attributes: + - key: ID + value: '9' + - key: Continent + value: SA + - key: City + value: Lima + - attributes: + - key: ID + value: '10' + - key: Continent + value: AF + - key: City + value: Cairo + - attributes: + - key: ID + value: '11' + - key: Continent + value: NA + - key: City + value: NewYork + - attributes: + - key: ID + value: '12' + - key: Continent + value: NA + - key: City + value: LosAngeles + - attributes: + - key: ID + value: '13' + - key: Continent + value: SA + - key: City + value: Lima +tests: + test: + policy: + replicas: + - count: 1 + selector: SPB + - count: 2 + selector: Americas + containerBackupFactor: 2 + selectors: + - name: SPB + count: 1 + clause: SAME + attribute: City + filter: SPBSSD + - name: Americas + count: 2 + clause: DISTINCT + attribute: City + filter: Americas + filters: + - name: SPBSSD + op: AND + filters: + - name: '' + key: Country + op: EQ + value: RU + filters: [] + - name: '' + key: City + op: EQ + value: St.Petersburg + filters: [] + - name: '' + key: SSD + op: EQ + value: '1' + filters: [] + - name: Americas + op: OR + filters: + - name: '' + key: Continent + op: EQ + value: NA + filters: [] + - name: '' + key: Continent + op: EQ + value: SA + filters: [] + result: + - - 1 + - 4 + - - 8 + - 12 + - 5 + - 10 diff --git a/client/src/test/resources/placement/non_strict.yml b/client/src/test/resources/placement/non_strict.yml new file mode 100644 index 0000000..a01986d --- /dev/null +++ b/client/src/test/resources/placement/non_strict.yml @@ -0,0 +1,52 @@ +name: non-strict selections +comment: These test specify loose selection behaviour, to allow fetching already PUT + objects even when there is not enough nodes to select from. +nodes: + - attributes: + - key: Country + value: Russia + - attributes: + - key: Country + value: Germany + - attributes: [] +tests: + not enough nodes (backup factor): + policy: + replicas: + - count: 1 + selector: MyStore + containerBackupFactor: 2 + selectors: + - name: MyStore + count: 2 + clause: DISTINCT + attribute: Country + filter: FromRU + filters: + - name: FromRU + key: Country + op: EQ + value: Russia + filters: [] + result: + - - 0 + not enough nodes (buckets): + policy: + replicas: + - count: 1 + selector: MyStore + containerBackupFactor: 1 + selectors: + - name: MyStore + count: 2 + clause: DISTINCT + attribute: Country + filter: FromRU + filters: + - name: FromRU + key: Country + op: EQ + value: Russia + filters: [] + result: + - - 0 diff --git a/client/src/test/resources/placement/rep_only.yml b/client/src/test/resources/placement/rep_only.yml new file mode 100644 index 0000000..b354591 --- /dev/null +++ b/client/src/test/resources/placement/rep_only.yml @@ -0,0 +1,62 @@ +name: REP X +nodes: + - publicKey: '' + addresses: [] + attributes: + - key: City + value: Saint-Petersburg + state: UNSPECIFIED + - publicKey: '' + addresses: [] + attributes: + - key: City + value: Moscow + state: UNSPECIFIED + - publicKey: '' + addresses: [] + attributes: + - key: City + value: Berlin + state: UNSPECIFIED + - publicKey: '' + addresses: [] + attributes: + - key: City + value: Paris + state: UNSPECIFIED +tests: + REP 1: + policy: + replicas: + - count: 1 + containerBackupFactor: 0 + selectors: [] + filters: [] + result: + - - 0 + - 1 + - 2 + REP 3: + policy: + replicas: + - count: 3 + containerBackupFactor: 0 + selectors: [] + filters: [] + result: + - - 0 + - 3 + - 1 + - 2 + REP 5: + policy: + replicas: + - count: 5 + containerBackupFactor: 0 + selectors: [] + filters: [] + result: + - - 0 + - 1 + - 2 + - 3 diff --git a/client/src/test/resources/placement/select_no_attribute.yml b/client/src/test/resources/placement/select_no_attribute.yml new file mode 100644 index 0000000..02046f3 --- /dev/null +++ b/client/src/test/resources/placement/select_no_attribute.yml @@ -0,0 +1,56 @@ +name: select with unspecified attribute +nodes: + - attributes: + - key: ID + value: '1' + - key: Country + value: RU + - key: City + value: St.Petersburg + - key: SSD + value: '0' + - attributes: + - key: ID + value: '2' + - key: Country + value: RU + - key: City + value: St.Petersburg + - key: SSD + value: '1' + - attributes: + - key: ID + value: '3' + - key: Country + value: RU + - key: City + value: Moscow + - key: SSD + value: '1' + - attributes: + - key: ID + value: '4' + - key: Country + value: RU + - key: City + value: Moscow + - key: SSD + value: '1' +tests: + test: + policy: + replicas: + - count: 1 + selector: X + containerBackupFactor: 1 + selectors: + - name: X + count: 4 + clause: DISTINCT + filter: '*' + filters: [] + result: + - - 0 + - 1 + - 2 + - 3 diff --git a/client/src/test/resources/placement/selector_invalid.yml b/client/src/test/resources/placement/selector_invalid.yml new file mode 100644 index 0000000..9b0a539 --- /dev/null +++ b/client/src/test/resources/placement/selector_invalid.yml @@ -0,0 +1,47 @@ +name: invalid selections +nodes: + - attributes: + - key: Country + value: Russia + - attributes: + - key: Country + value: Germany + - attributes: [] +tests: + missing filter: + policy: + replicas: + - count: 1 + selector: MyStore + containerBackupFactor: 1 + selectors: + - name: MyStore + count: 1 + clause: DISTINCT + attribute: Country + filter: FromNL + filters: + - name: FromRU + key: Country + op: EQ + value: Russia + filters: [] + error: filter not found + not enough nodes (filter results in empty set): + policy: + replicas: + - count: 1 + selector: MyStore + containerBackupFactor: 2 + selectors: + - name: MyStore + count: 2 + clause: DISTINCT + attribute: Country + filter: FromMoon + filters: + - name: FromMoon + key: Country + op: EQ + value: Moon + filters: [] diff --git a/exceptions/src/main/java/info/frostfs/sdk/constants/ErrorConst.java b/exceptions/src/main/java/info/frostfs/sdk/constants/ErrorConst.java index 00c1711..8c248c9 100644 --- a/exceptions/src/main/java/info/frostfs/sdk/constants/ErrorConst.java +++ b/exceptions/src/main/java/info/frostfs/sdk/constants/ErrorConst.java @@ -59,6 +59,18 @@ public class ErrorConst { public static final String STRING_IS_TOO_BIG_TEMPLATE = "string size is too big=%s"; public static final String STRING_SIZE_IS_INVALID_TEMPLATE = "invalid string size=%s"; + public static final String FILTER_NAME_IS_EMPTY = "Filter name for selector is empty"; + public static final String INVALID_FILTER_NAME_TEMPLATE = "filter name is invalid: '%s' is reserved"; + public static final String INVALID_FILTER_OPERATION_TEMPLATE = "invalid filter operation: %s"; + public static final String FILTER_NOT_FOUND = "filter not found"; + public static final String FILTER_NOT_FOUND_TEMPLATE = "filter not found: SELECT FROM '%s'"; + public static final String NON_EMPTY_FILTERS = "simple filter contains sub-filters"; + public static final String NOT_ENOUGH_NODES = "not enough nodes"; + public static final String NOT_ENOUGH_NODES_TEMPLATE = "not enough nodes to SELECT from '%s'"; + public static final String UNNAMED_TOP_FILTER = "unnamed top-level filter"; + public static final String VECTORS_IS_NULL = "vectors cannot be null"; + public static final String SELECTOR_NOT_FOUND_TEMPLATE = "selector not found: %s"; + private ErrorConst() { } } diff --git a/models/src/main/java/info/frostfs/sdk/constants/AppConst.java b/models/src/main/java/info/frostfs/sdk/constants/AppConst.java index feb1ca5..58b0041 100644 --- a/models/src/main/java/info/frostfs/sdk/constants/AppConst.java +++ b/models/src/main/java/info/frostfs/sdk/constants/AppConst.java @@ -1,5 +1,7 @@ package info.frostfs.sdk.constants; +import java.math.BigInteger; + public class AppConst { public static final String RESERVED_PREFIX = "__SYSTEM__"; @@ -15,6 +17,8 @@ public class AppConst { public static final int DEFAULT_GRPC_TIMEOUT = 5; public static final long DEFAULT_POLL_INTERVAL = 10; + public static final BigInteger UNSIGNED_LONG_MASK = BigInteger.ONE.shiftLeft(Long.SIZE).subtract(BigInteger.ONE); + private AppConst() { } } diff --git a/models/src/main/java/info/frostfs/sdk/constants/AttributeConst.java b/models/src/main/java/info/frostfs/sdk/constants/AttributeConst.java index 214a9e3..964b3d4 100644 --- a/models/src/main/java/info/frostfs/sdk/constants/AttributeConst.java +++ b/models/src/main/java/info/frostfs/sdk/constants/AttributeConst.java @@ -5,6 +5,17 @@ import static info.frostfs.sdk.constants.AppConst.RESERVED_PREFIX; public class AttributeConst { public static final String DISABLE_HOMOMORPHIC_HASHING_ATTRIBUTE = RESERVED_PREFIX + "DISABLE_HOMOMORPHIC_HASHING"; + /* + * ATTRIBUTE_PRICE is a key to the node attribute that indicates + * the price in GAS tokens for storing one GB of data during one Epoch. + * */ + public static final String ATTRIBUTE_PRICE = "Price"; + + /* + * ATTRIBUTE_CAPACITY is a key to the node attribute that indicates the total available disk space in Gigabytes. + * */ + public static final String ATTRIBUTE_CAPACITY = "Capacity"; + private AttributeConst() { } } diff --git a/models/src/main/java/info/frostfs/sdk/dto/netmap/Filter.java b/models/src/main/java/info/frostfs/sdk/dto/netmap/Filter.java index fe809fe..a957dce 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/netmap/Filter.java +++ b/models/src/main/java/info/frostfs/sdk/dto/netmap/Filter.java @@ -1,6 +1,6 @@ package info.frostfs.sdk.dto.netmap; -import info.frostfs.sdk.enums.FilterOperation; +import info.frostfs.sdk.enums.netmap.FilterOperation; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/models/src/main/java/info/frostfs/sdk/dto/netmap/Hasher.java b/models/src/main/java/info/frostfs/sdk/dto/netmap/Hasher.java new file mode 100644 index 0000000..286f77f --- /dev/null +++ b/models/src/main/java/info/frostfs/sdk/dto/netmap/Hasher.java @@ -0,0 +1,5 @@ +package info.frostfs.sdk.dto.netmap; + +public interface Hasher { + long getHash(); +} diff --git a/models/src/main/java/info/frostfs/sdk/dto/netmap/NodeInfo.java b/models/src/main/java/info/frostfs/sdk/dto/netmap/NodeInfo.java index a3d28f8..a46cf2a 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/netmap/NodeInfo.java +++ b/models/src/main/java/info/frostfs/sdk/dto/netmap/NodeInfo.java @@ -1,20 +1,31 @@ package info.frostfs.sdk.dto.netmap; -import info.frostfs.sdk.enums.NodeState; +import info.frostfs.sdk.enums.netmap.NodeState; import lombok.Getter; +import org.apache.commons.codec.digest.MurmurHash3; +import java.math.BigInteger; import java.util.Collections; import java.util.List; import java.util.Map; +import static info.frostfs.sdk.constants.AppConst.UNSIGNED_LONG_MASK; +import static info.frostfs.sdk.constants.AttributeConst.ATTRIBUTE_CAPACITY; +import static info.frostfs.sdk.constants.AttributeConst.ATTRIBUTE_PRICE; +import static java.util.Objects.isNull; + @Getter -public class NodeInfo { +public class NodeInfo implements Hasher { private final NodeState state; private final Version version; private final List addresses; private final Map attributes; private final byte[] publicKey; + private long hash; + private BigInteger price = UNSIGNED_LONG_MASK; + + public NodeInfo(NodeState state, Version version, List addresses, Map attributes, byte[] publicKey) { this.state = state; @@ -23,4 +34,26 @@ public class NodeInfo { this.attributes = Collections.unmodifiableMap(attributes); this.publicKey = publicKey; } + + public long getHash() { + if (hash == 0) { + hash = MurmurHash3.hash128x64(publicKey, 0, publicKey.length, 0)[0]; + } + + return hash; + } + + public BigInteger getCapacity() { + var capacity = attributes.get(ATTRIBUTE_CAPACITY); + return isNull(capacity) ? BigInteger.valueOf(0) : new BigInteger(capacity); + } + + public BigInteger getPrice() { + if (price.equals(UNSIGNED_LONG_MASK)) { + var priceString = attributes.get(ATTRIBUTE_PRICE); + price = isNull(priceString) ? BigInteger.valueOf(0) : new BigInteger(priceString); + } + + return price; + } } diff --git a/models/src/main/java/info/frostfs/sdk/dto/netmap/Replica.java b/models/src/main/java/info/frostfs/sdk/dto/netmap/Replica.java index 5c28462..09d059d 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/netmap/Replica.java +++ b/models/src/main/java/info/frostfs/sdk/dto/netmap/Replica.java @@ -13,6 +13,14 @@ import static info.frostfs.sdk.constants.FieldConst.EMPTY_STRING; public class Replica { private final int count; private final String selector; + private long ecDataCount; + private long ecParityCount; + + public Replica(int count, String selector, int ecDataCount, int ecParityCount) { + this(count, selector); + this.ecDataCount = Integer.toUnsignedLong(ecDataCount); + this.ecParityCount = Integer.toUnsignedLong(ecParityCount); + } public Replica(int count, String selector) { if (count <= 0) { @@ -32,8 +40,11 @@ public class Replica { ); } - this.count = count; this.selector = EMPTY_STRING; } + + public int getCountNodes() { + return count != 0 ? count : (int) (ecDataCount + ecParityCount); + } } diff --git a/models/src/main/java/info/frostfs/sdk/dto/netmap/Selector.java b/models/src/main/java/info/frostfs/sdk/dto/netmap/Selector.java index 71197a1..4dee15c 100644 --- a/models/src/main/java/info/frostfs/sdk/dto/netmap/Selector.java +++ b/models/src/main/java/info/frostfs/sdk/dto/netmap/Selector.java @@ -1,15 +1,17 @@ package info.frostfs.sdk.dto.netmap; -import info.frostfs.sdk.enums.SelectorClause; +import info.frostfs.sdk.enums.netmap.SelectorClause; import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.Setter; @Getter +@Setter @AllArgsConstructor public class Selector { private final String name; - private final int count; - private final SelectorClause clause; - private final String attribute; - private final String filter; + private int count; + private SelectorClause clause; + private String attribute; + private String filter; } diff --git a/models/src/main/java/info/frostfs/sdk/enums/FilterOperation.java b/models/src/main/java/info/frostfs/sdk/enums/netmap/FilterOperation.java similarity index 95% rename from models/src/main/java/info/frostfs/sdk/enums/FilterOperation.java rename to models/src/main/java/info/frostfs/sdk/enums/netmap/FilterOperation.java index f49b0f2..69513c8 100644 --- a/models/src/main/java/info/frostfs/sdk/enums/FilterOperation.java +++ b/models/src/main/java/info/frostfs/sdk/enums/netmap/FilterOperation.java @@ -1,4 +1,4 @@ -package info.frostfs.sdk.enums; +package info.frostfs.sdk.enums.netmap; import java.util.Collections; import java.util.HashMap; diff --git a/models/src/main/java/info/frostfs/sdk/enums/NodeState.java b/models/src/main/java/info/frostfs/sdk/enums/netmap/NodeState.java similarity index 94% rename from models/src/main/java/info/frostfs/sdk/enums/NodeState.java rename to models/src/main/java/info/frostfs/sdk/enums/netmap/NodeState.java index 3f833e3..ac6ae78 100644 --- a/models/src/main/java/info/frostfs/sdk/enums/NodeState.java +++ b/models/src/main/java/info/frostfs/sdk/enums/netmap/NodeState.java @@ -1,4 +1,4 @@ -package info.frostfs.sdk.enums; +package info.frostfs.sdk.enums.netmap; import java.util.Collections; import java.util.HashMap; diff --git a/models/src/main/java/info/frostfs/sdk/enums/SelectorClause.java b/models/src/main/java/info/frostfs/sdk/enums/netmap/SelectorClause.java similarity index 94% rename from models/src/main/java/info/frostfs/sdk/enums/SelectorClause.java rename to models/src/main/java/info/frostfs/sdk/enums/netmap/SelectorClause.java index b10ff0c..f375f88 100644 --- a/models/src/main/java/info/frostfs/sdk/enums/SelectorClause.java +++ b/models/src/main/java/info/frostfs/sdk/enums/netmap/SelectorClause.java @@ -1,4 +1,4 @@ -package info.frostfs.sdk.enums; +package info.frostfs.sdk.enums.netmap; import java.util.Collections; import java.util.HashMap; diff --git a/models/src/main/java/info/frostfs/sdk/mappers/netmap/FilterMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/netmap/FilterMapper.java index 3f63b3b..529b26d 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/netmap/FilterMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/netmap/FilterMapper.java @@ -2,7 +2,7 @@ package info.frostfs.sdk.mappers.netmap; import frostfs.netmap.Types; import info.frostfs.sdk.dto.netmap.Filter; -import info.frostfs.sdk.enums.FilterOperation; +import info.frostfs.sdk.enums.netmap.FilterOperation; import info.frostfs.sdk.exceptions.ProcessFrostFSException; import info.frostfs.sdk.exceptions.ValidationFrostFSException; import org.apache.commons.collections4.CollectionUtils; diff --git a/models/src/main/java/info/frostfs/sdk/mappers/netmap/NodeInfoMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/netmap/NodeInfoMapper.java index f319583..c28cd35 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/netmap/NodeInfoMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/netmap/NodeInfoMapper.java @@ -4,7 +4,7 @@ import frostfs.netmap.Service; import frostfs.netmap.Types; import frostfs.netmap.Types.NodeInfo.Attribute; import info.frostfs.sdk.dto.netmap.NodeInfo; -import info.frostfs.sdk.enums.NodeState; +import info.frostfs.sdk.enums.netmap.NodeState; import info.frostfs.sdk.exceptions.ProcessFrostFSException; import java.util.stream.Collectors; diff --git a/models/src/main/java/info/frostfs/sdk/mappers/netmap/ReplicaMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/netmap/ReplicaMapper.java index 959b9d6..3ac0105 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/netmap/ReplicaMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/netmap/ReplicaMapper.java @@ -36,15 +36,17 @@ public class ReplicaMapper { return Types.Replica.newBuilder() .setCount(replica.getCount()) .setSelector(replica.getSelector()) + .setEcDataCount((int) replica.getEcDataCount()) + .setEcParityCount((int) replica.getEcParityCount()) .build(); } - public static Replica[] toModels(List filters) { - if (CollectionUtils.isEmpty(filters)) { + public static Replica[] toModels(List replicas) { + if (CollectionUtils.isEmpty(replicas)) { return null; } - return filters.stream().map(ReplicaMapper::toModel).toArray(Replica[]::new); + return replicas.stream().map(ReplicaMapper::toModel).toArray(Replica[]::new); } public static Replica toModel(Types.Replica replica) { @@ -52,6 +54,11 @@ public class ReplicaMapper { return null; } - return new Replica(replica.getCount(), replica.getSelector()); + return new Replica( + replica.getCount(), + replica.getSelector(), + replica.getEcDataCount(), + replica.getEcParityCount() + ); } } diff --git a/models/src/main/java/info/frostfs/sdk/mappers/netmap/SelectorMapper.java b/models/src/main/java/info/frostfs/sdk/mappers/netmap/SelectorMapper.java index 8b9067d..462519a 100644 --- a/models/src/main/java/info/frostfs/sdk/mappers/netmap/SelectorMapper.java +++ b/models/src/main/java/info/frostfs/sdk/mappers/netmap/SelectorMapper.java @@ -2,7 +2,7 @@ package info.frostfs.sdk.mappers.netmap; import frostfs.netmap.Types; import info.frostfs.sdk.dto.netmap.Selector; -import info.frostfs.sdk.enums.SelectorClause; +import info.frostfs.sdk.enums.netmap.SelectorClause; import info.frostfs.sdk.exceptions.ProcessFrostFSException; import info.frostfs.sdk.exceptions.ValidationFrostFSException; import org.apache.commons.collections4.CollectionUtils; diff --git a/models/src/test/java/info/frostfs/sdk/mappers/netmap/FilterMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/netmap/FilterMapperTest.java index 4b1649d..788d71e 100644 --- a/models/src/test/java/info/frostfs/sdk/mappers/netmap/FilterMapperTest.java +++ b/models/src/test/java/info/frostfs/sdk/mappers/netmap/FilterMapperTest.java @@ -2,7 +2,7 @@ package info.frostfs.sdk.mappers.netmap; import frostfs.netmap.Types; import info.frostfs.sdk.dto.netmap.Filter; -import info.frostfs.sdk.enums.FilterOperation; +import info.frostfs.sdk.enums.netmap.FilterOperation; import info.frostfs.sdk.exceptions.ProcessFrostFSException; import info.frostfs.sdk.exceptions.ValidationFrostFSException; import org.junit.jupiter.api.Test; diff --git a/models/src/test/java/info/frostfs/sdk/mappers/netmap/NodeInfoMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/netmap/NodeInfoMapperTest.java index e2b6c29..dd2dc1f 100644 --- a/models/src/test/java/info/frostfs/sdk/mappers/netmap/NodeInfoMapperTest.java +++ b/models/src/test/java/info/frostfs/sdk/mappers/netmap/NodeInfoMapperTest.java @@ -3,7 +3,7 @@ package info.frostfs.sdk.mappers.netmap; import com.google.protobuf.ByteString; import frostfs.netmap.Service; import frostfs.netmap.Types; -import info.frostfs.sdk.enums.NodeState; +import info.frostfs.sdk.enums.netmap.NodeState; import info.frostfs.sdk.exceptions.ProcessFrostFSException; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; diff --git a/models/src/test/java/info/frostfs/sdk/mappers/netmap/SelectorMapperTest.java b/models/src/test/java/info/frostfs/sdk/mappers/netmap/SelectorMapperTest.java index 4e55ae0..6d46553 100644 --- a/models/src/test/java/info/frostfs/sdk/mappers/netmap/SelectorMapperTest.java +++ b/models/src/test/java/info/frostfs/sdk/mappers/netmap/SelectorMapperTest.java @@ -2,7 +2,7 @@ package info.frostfs.sdk.mappers.netmap; import frostfs.netmap.Types; import info.frostfs.sdk.dto.netmap.Selector; -import info.frostfs.sdk.enums.SelectorClause; +import info.frostfs.sdk.enums.netmap.SelectorClause; import info.frostfs.sdk.exceptions.ProcessFrostFSException; import info.frostfs.sdk.exceptions.ValidationFrostFSException; import org.junit.jupiter.api.Test; diff --git a/pom.xml b/pom.xml index 978fea2..40f8790 100644 --- a/pom.xml +++ b/pom.xml @@ -17,7 +17,7 @@ - 0.10.0 + 0.11.0 11 11 @@ -42,6 +42,11 @@ commons-lang3 3.14.0 + + commons-codec + commons-codec + 1.18.0 + org.projectlombok lombok @@ -78,6 +83,11 @@ ${mockito.version} test + + org.yaml + snakeyaml + 2.4 + From 56c8795e70a49894011d1bfdf79c5c2b83d68a73 Mon Sep 17 00:00:00 2001 From: Ori Bruk Date: Fri, 25 Apr 2025 19:15:16 +0300 Subject: [PATCH 38/38] [#54] Fix patch logic Signed-off-by: Ori Bruk --- CHANGELOG.md | 25 +++++++ .../object/patch/PrmObjectPatch.java | 14 ++-- .../sdk/services/impl/ObjectClientImpl.java | 66 ++++++++++++------- pom.xml | 2 +- 4 files changed, 76 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 389b85b..e859051 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,30 @@ # Changelog +## [0.12.0] - 2025-04-24 + +### Fixed + +- Patch logic +- Patch payload requirements + +## [0.11.0] - 2025-04-23 + +### Added + +- Placement policy vectors + +## [0.10.0] - 2025-03-10 + +### Added + +- Auto deploy to forgejo + +## [0.9.0] - 2025-03-05 + +### Added + +- APE rule deserializer + ## [0.9.0] - 2025-03-05 ### Added diff --git a/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/patch/PrmObjectPatch.java b/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/patch/PrmObjectPatch.java index 9f93c9a..58849df 100644 --- a/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/patch/PrmObjectPatch.java +++ b/client/src/main/java/info/frostfs/sdk/jdo/parameters/object/patch/PrmObjectPatch.java @@ -6,9 +6,7 @@ import info.frostfs.sdk.dto.object.patch.Address; import info.frostfs.sdk.dto.object.patch.Range; import info.frostfs.sdk.dto.session.SessionToken; import info.frostfs.sdk.jdo.parameters.session.SessionContext; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; +import lombok.*; import java.io.InputStream; import java.util.List; @@ -21,12 +19,8 @@ public class PrmObjectPatch implements SessionContext { @NotNull private Address address; - @NotNull private Range range; - - @NotNull private InputStream payload; - private List newAttributes; private boolean replaceAttributes; private int maxChunkLength; @@ -39,4 +33,10 @@ public class PrmObjectPatch implements SessionContext { this.payload = payload; this.maxChunkLength = maxChunkLength; } + + public PrmObjectPatch(Address address, List newAttributes, boolean replaceAttributes) { + this.address = address; + this.newAttributes = newAttributes; + this.replaceAttributes = replaceAttributes; + } } diff --git a/client/src/main/java/info/frostfs/sdk/services/impl/ObjectClientImpl.java b/client/src/main/java/info/frostfs/sdk/services/impl/ObjectClientImpl.java index 46b2905..71741e8 100644 --- a/client/src/main/java/info/frostfs/sdk/services/impl/ObjectClientImpl.java +++ b/client/src/main/java/info/frostfs/sdk/services/impl/ObjectClientImpl.java @@ -231,21 +231,29 @@ public class ObjectClientImpl extends ContextAccessor implements ObjectClient { public ObjectId patchObject(PrmObjectPatch args, CallContext ctx) { validate(args); - var request = createInitPatchRequest(args); - var protoToken = RequestConstructor.createObjectTokenContext( - getOrCreateSession(args, ctx), - request.getBody().getAddress(), - frostfs.session.Types.ObjectSessionContext.Verb.PATCH, - getContext().getKey() - ); - - var currentPos = args.getRange().getOffset(); - var chunkSize = args.getMaxChunkLength(); - byte[] chunkBuffer = new byte[chunkSize]; - var service = deadLineAfter(objectServiceClient, ctx.getTimeout(), ctx.getTimeUnit()); PatchStreamer writer = new PatchStreamer(service); + var request = createInitPatchRequest(args, ctx); + writer.write(request.build()); + + if (nonNull(args.getPayload())) { + patchObjectPayload(request, args, writer); + } + + var response = writer.complete(); + + Verifier.checkResponse(response); + + return ObjectIdMapper.toModel(response.getBody().getObjectId()); + } + + private void patchObjectPayload(Service.PatchRequest.Builder request, PrmObjectPatch args, PatchStreamer writer) { + var currentPos = args.getRange().getOffset(); + + var chunkSize = args.getMaxChunkLength() > 0 ? args.getMaxChunkLength() : AppConst.OBJECT_CHUNK_SIZE; + byte[] chunkBuffer = new byte[chunkSize]; + var bytesCount = readNBytes(args.getPayload(), chunkBuffer, chunkSize); while (bytesCount > 0) { var range = Service.Range.newBuilder() @@ -253,25 +261,25 @@ public class ObjectClientImpl extends ContextAccessor implements ObjectClient { .setLength(bytesCount) .build(); - Service.PatchRequest.Body.Patch.newBuilder() + var patch = Service.PatchRequest.Body.Patch.newBuilder() .setChunk(ByteString.copyFrom(chunkBuffer, 0, bytesCount)) .setSourceRange(range) .build(); - currentPos += bytesCount; + var body = Service.PatchRequest.Body.newBuilder() + .setAddress(request.getBody().getAddress()) + .setPatch(patch) + .build(); + request.setBody(body); - RequestConstructor.addMetaHeader(request, args.getXHeaders(), protoToken); + RequestConstructor.addMetaHeader(request, args.getXHeaders(), request.getMetaHeader().getSessionToken()); sign(request, getContext().getKey()); writer.write(request.build()); + + currentPos += bytesCount; bytesCount = readNBytes(args.getPayload(), chunkBuffer, chunkSize); } - - var response = writer.complete(); - - Verifier.checkResponse(response); - - return ObjectIdMapper.toModel(response.getBody().getObjectId()); } private ObjectFrostFS getObject(Service.GetRequest request, CallContext ctx) { @@ -635,14 +643,26 @@ public class ObjectClientImpl extends ContextAccessor implements ObjectClient { return request.build(); } - private Service.PatchRequest.Builder createInitPatchRequest(PrmObjectPatch args) { + private Service.PatchRequest.Builder createInitPatchRequest(PrmObjectPatch args, CallContext ctx) { var address = AddressMapper.toGrpcMessage(args.getAddress()); var body = Service.PatchRequest.Body.newBuilder() .setAddress(address) .setReplaceAttributes(args.isReplaceAttributes()) .addAllNewAttributes(ObjectAttributeMapper.toGrpcMessages(args.getNewAttributes())) .build(); - return Service.PatchRequest.newBuilder() + var request = Service.PatchRequest.newBuilder() .setBody(body); + + var protoToken = RequestConstructor.createObjectTokenContext( + getOrCreateSession(args, ctx), + request.getBody().getAddress(), + frostfs.session.Types.ObjectSessionContext.Verb.PATCH, + getContext().getKey() + ); + + RequestConstructor.addMetaHeader(request, args.getXHeaders(), protoToken); + sign(request, getContext().getKey()); + + return request; } } diff --git a/pom.xml b/pom.xml index 40f8790..d8de384 100644 --- a/pom.xml +++ b/pom.xml @@ -17,7 +17,7 @@ - 0.11.0 + 0.12.0 11 11