diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index c7dc2b1..9efb74e 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -6,7 +6,7 @@ labels: kind/bug assignees: '' --- -- [ ] I have searched the [issues](https://github.com/reactivegroup/capa/issues) of this repository and believe that this is not a duplicate. +- [ ] I have searched the [issues](https://github.com/capa-cloud/capa-java/issues) of this repository and believe that this is not a duplicate. ### Environment @@ -38,4 +38,4 @@ assignees: '' -RELEASE NOTE: \ No newline at end of file +RELEASE NOTE: diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 755eca8..b6d0385 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -1,126 +1,43 @@ -# Define the workflow's name -name: "Build and Test" -# Triggered after push, pull_request and workflow_dispatch events -on: [push, pull_request, workflow_dispatch] +name: Build and Test -# Define the environment variables -env: - FAIL_FAST: 0 - SHOW_ERROR_DETAIL: 1 +on: + push: + pull_request: + workflow_dispatch: -jobs: - build-source: - name: "Build Source Code" - runs-on: ubuntu-latest - env: - # The default JDK version - JDK_VER: 8 - outputs: - version: ${{ steps.capa-version.outputs.version }} - steps: - - name: "Checkout the source code" - uses: actions/checkout@v2 - with: - path: capa - - name: "Set up OpenJDK ${{ env.JDK_VER }}" - uses: actions/setup-java@v1 - with: - java-version: ${{ env.JDK_VER }} - - name: "Cache local Maven repository" - uses: actions/cache@v2 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - - name: "Cache capa" - uses: actions/cache@v2 - with: - path: ~/.m2/repository/group/rxcloud/capa - key: ${{ runner.os }}-capa-snapshot-${{ github.sha }} - - name: "Build capa" - run: | - cd ${{ github.workspace }}/capa - mvn clean - mvn --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean source:jar install -Pjacoco,rat,checkstyle -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.count=5 -Dmaven.test.skip=true -Dmaven.test.skip.exec=true -Dgpg.skip=true - - name: "Pack rat file if failure" - if: failure() - run: 7z a ${{ github.workspace }}/rat.zip *rat.txt -r - - name: "Upload rat file if failure" - if: failure() - uses: actions/upload-artifact@v2 - with: - name: "rat-file" - path: ${{ github.workspace }}/rat.zip - - name: "Pack checkstyle file if failure" - if: failure() - run: 7z a ${{ github.workspace }}/checkstyle.zip *checkstyle* -r - - name: "Upload checkstyle file if failure" - if: failure() - uses: actions/upload-artifact@v2 - with: - name: "checkstyle-file" - path: ${{ github.workspace }}/checkstyle.zip +permissions: + contents: read - coverage: - name: "Code Coverage" - needs: [ build-source ] +jobs: + test: + name: Java ${{ matrix.java }} / Logback ${{ matrix.logback }} runs-on: ubuntu-latest - steps: - - name: "Checkout the source code" - uses: actions/checkout@v2 - with: - path: capa - - name: "Set up OpenJDK 8" - uses: actions/setup-java@v1 - with: - distribution: 'adopt' - java-version: 8 - - uses: actions/cache@v2 - name: "Cache local Maven repository" - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- - - name: "Calculate code coverage" - run: | - cd ${{ github.workspace }}/capa - mvn --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean test verify -Pjacoco -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.count=5 -DskipTests=false -DskipIntegrationTests=false -Dcheckstyle.skip=true -Drat.skip=true -Dmaven.javadoc.skip=true -Dgpg.skip=true - - name: "Upload to Codecov" - uses: codecov/codecov-action@v1 - with: - token: ${{ secrets.CODECOV_TOKEN }} - file: ./**/target/site/jacoco/jacoco.xml - name: codecov - - unit-test: - needs: [ build-source ] - name: "Unit Test On ${{ matrix.os }} (OpenJDK: ${{ matrix.jdk }})" - runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: - os: [ ubuntu-latest ] - jdk: [ 8, 11 ] - env: - DISABLE_FILE_SYSTEM_TEST: true + include: + - java: "8" + logback: "1.3.16" + - java: "11" + logback: "1.3.16" + - java: "11" + logback: "1.5.38" steps: - - name: "Checkout the source code" - uses: actions/checkout@v2 - with: - path: capa - - name: "Set up OpenJDK ${{ matrix.jdk }}" - uses: actions/setup-java@v1 - with: - java-version: ${{ matrix.jdk }} - - uses: actions/cache@v2 - name: "Cache local Maven repository" - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- - - name: "Unit Test" - timeout-minutes: 10 - run: | - cd ${{ github.workspace }}/capa - mvn --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean test verify -Pjacoco,rat,checkstyle -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.count=5 -DskipTests=false -Dcheckstyle.skip=false -Drat.skip=false -Dmaven.javadoc.skip=true -Dgpg.skip=true + - name: Check out source + uses: actions/checkout@v7 + - name: Set up Java ${{ matrix.java }} + uses: actions/setup-java@v5 + with: + distribution: temurin + java-version: ${{ matrix.java }} + cache: maven + - name: Verify + run: >- + mvn --batch-mode --no-transfer-progress --fail-fast clean verify + -Pjacoco,rat,checkstyle + -DskipTests=false + -Dcheckstyle.skip=false + -Drat.skip=false + -Dlogback.version=${{ matrix.logback }} + -Dmaven.javadoc.skip=true + -Dgpg.skip=true diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..823c193 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,7 @@ +# Repository Guidelines + +- GitHub: `capa-cloud/capa-java` +- Category: Java SDK/runtime project. +- Public documentation: `https://capa.rxcloud.group/` + +Use Maven/Java project conventions already present in the repository. Do not change public SDK APIs without updating examples and compatibility notes. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index 7bc5757..8df7352 100644 --- a/README.md +++ b/README.md @@ -1,212 +1,156 @@ -![logo](./docs/logo.png) +

+ Capa +

-# Capa(cloud application api): To be the high-level api layer for all application runtime. +# Capa Java SDK -Let the code achieve "write once, run anywhere". +Capa is a rich-SDK implementation of Cloud Application APIs for Java. Applications program against vendor-neutral runtime interfaces while pluggable SPI modules connect those interfaces to concrete middleware or cloud services. -With the help of the Capa project, your Java applications have the ability to run across clouds and hybrid clouds with small changes. +[简体中文](README_ZH.md) · [Documentation](https://capa.rxcloud.group/) · [Issues](https://github.com/capa-cloud/capa-java/issues) -[中文文档](./README_ZH.md) +> **Project boundary:** this repository contains the Java SDK. The experimental Go sidecar runtime is maintained separately in [capa](https://github.com/capa-cloud/capa), and API contracts are defined in [cloud-runtimes-jvm](https://github.com/capa-cloud/cloud-runtimes-jvm). -## Motivation +## Requirements -### Mecha architecture +- Java 8 or 11 +- Maven 3.8.1 or later +- A Capa SPI implementation for every capability used by the application -The Capa project is based on the design concept of the Mecha architecture and uses **rich SDK mode** to provide Multi-Runtime standard API. +### Logging compatibility -You can simply understand the Capa project as the SDK implementation of [Dapr](https://github.com/dapr/dapr) / [Layotto](https://github.com/mosn/layotto) Sidecar mode projects. +The Java 8-compatible build pins Logback `1.3.16`. Upstream classifies the +entire Logback 1.3.x line as end-of-life; maintained Logback 1.5.x releases +require Java 11 or later. Applications that use the Capa Logback integration +should choose one of these paths: -To understand the design ideas of Mecha architecture, please read the following articles: +- On Java 11 or later, manage `logback-core` and `logback-classic` to `1.5.38` + or a later compatible 1.5.x release. CI verifies the repository with + `-Dlogback.version=1.5.38`. +- On Java 8, use the actively maintained Log4j 2 integration where possible, + or plan a Java 11 migration before accepting untrusted logging + configuration. -[死生之地不可不察:论API标准化对Dapr的重要性](https://www.infoq.cn/article/wjkNGoGaaHyKs7xIyTSB) +See the [Logback download and support status](https://logback.qos.ch/download.html) +for the current runtime requirements. Do not load logging configuration from +untrusted sources. -[MOSN子项目Layotto:开启服务网格+应用运行时新篇章](http://mosn.io/layotto/#/zh/blog/mosn-subproject-layotto-opening-a-new-chapter-in-service-grid-application-runtime/index) +## Add the SDK -### Sidecar or SDK - -Based on the Mecha architecture concept, Multi-Runtime provides standard API functions in a Sidecar manner, which seems to be the most reasonable choice. - -So why not use Dapr/Layotto and other projects directly, but choose to develop the Capa project of **Rich SDK Mode** instead. - -Summary: _The Sidecar architecture represented by Dapr is the future, but it is difficult for many existing enterprises and systems to upgrade to the Sidecar architecture in one step. The rich SDK architecture will exist for a long time._ - -Extension: _Faced with the huge Java systems, the Capa project will use the rich SDK model to support the transition from the Java system to the Mecha architecture. After Dapr and other projects mature, they can also be seamlessly connected to the Sidecar architecture._ - -For specific discussions on this issue, please refer to: - -[SDK模型的Dapr API](https://github.com/dapr/dapr/issues/3261) - -[Dapr API的未来计划](https://github.com/dapr/dapr/issues/2817) - -[Java SDK的设计讨论](https://github.com/mosn/layotto/issues/188) - -## Feature - -### API definition - -Capa API design follow community standards, please refer to the API definitions of open source projects such as Dapr / Layotto. - -The API definition is placed in the following independent warehouse, unbound from the Capa project, and hopes to develop into the community's API standard definition: - -+ java: [cloud-runtimes-jvm](https://github.com/reactivegroup/cloud-runtimes-jvm) -+ python(alpha): [cloud-runtimes-python](https://github.com/reactivegroup/cloud-runtimes-python) -+ golang(alpha): [cloud-runtimes-golang](https://github.com/reactivegroup/cloud-runtimes-golang) - -#### Why not use Dapr API directly? - -Due to the current strong binding between Dapr API and Dapr project, we hope that this set of API can become the standard of the entire community, so Capa puts the API definition in an independent warehouse and keeps it synchronized with upstream community standards at all times. - -We hope that Dapr can deploy its API independently, decouple it from the Dapr project, and become a standard for the entire community. - -For the discussion of this item, please see: - -[Future plans for dapr api](https://github.com/dapr/dapr/issues/2817) - -### Capa features - -Capa (Java SDK) is an SDK solution that implements Mecha architecture for Java applications. It currently supports features in the following areas: - -+ Service Invocation (RPC) -+ Configuration Centor (Configuration) -+ Publish/Subscribe (Pub/Sub) -+ State Management (State) -+ Application Log/Metrics/Traces (Telemetry) -+ Database (SQL) -alpha -+ Schedule (Schedule) -alpha -+ ... - -## Design - -### Capa design - -Design idea: **Standard API + pluggable and replaceable SDK components** mode - -In different distributed middleware fields, Capa provides a unified standard programming API without relying on specific middleware APIs. Therefore, the application does not need to rely on any specific middleware API when programming with Capa, but only needs to rely on Capa's standard programming API. - -When deployed to different target environments, Capa will load different implementation classes of the standard API into the application. When calling a unified programming API, the underlying runtime will be adapted to different specific middleware SDK implementations. - -The middleware team needs to develop the implementation classes of the standard API in the target environment for different target environments; and the application code can have a "write once, run anywhere" development experience. - -### SDK design - -The Capa module is divided into the following parts: - -* sdk -* sdk-component -* sdk-spi -* sdk-spi-demo/... - -![capa-design](./docs/capa-design/capa-layer.PNG) - -Application programming only needs to rely on the SDK, and use the unified programming API defined in the SDK module. - -Before running, the specific SPI implementation package will be introduced as a specific implementation of the unified programming API. - -## Usage - -### Getting Started - -#### Importing Capa's Java SDK - -For a Maven project, add the following to your pom.xml file: +Current repository version: `1.11.13.2.RELEASE`. ```xml - - - ... - - ... - - - group.rxcloud - capa-sdk - 1.0.7.RELEASE - - ... - - ... - + + group.rxcloud + capa-sdk + 1.11.13.2.RELEASE + ``` -Sample implementation library: +`capa-sdk` exposes the programming surface but does not select production infrastructure on its own. Add a compatible SPI implementation, such as an organization-specific adapter or one of the cloud integration repositories. -```xml +For local exploration only, the repository publishes a demo SPI: - - ... - - ... - - - group.rxcloud - capa-sdk-spi-demo - 1.0.7.RELEASE - - ... - - ... - +```xml + + group.rxcloud + capa-sdk-spi-demo + 1.11.13.2.RELEASE + runtime + ``` -### Running the examples - -Try the following examples to learn more about Capa's Java SDK: - -* [capa-demo](https://github.com/reactivegroup/capa/tree/master/sdk-spi-demo) -* [capa-aws](https://github.com/reactivegroup/capa-aws) -* [capa-alibaba](https://github.com/reactivegroup/capa-alibaba) +The demo SPI is an example implementation, not a production runtime. -### Low retrofit cost migration +## Capabilities -If you want to use the native Capa API, your legacy system needs to face a large refactoring workload. +The SDK and API modules cover these runtime domains: -In order to make the migration low-cost, we can reuse the middleware API currently used. +| Domain | Purpose | Maturity note | +| --- | --- | --- | +| RPC | Service invocation | API and component/SPI layers are present | +| Configuration | Dynamic configuration stores | API and component/SPI layers are present | +| Pub/Sub | Message publication and subscription | API and component/SPI layers are present | +| State | Key-value state operations | API contracts are present; adapter support varies | +| Telemetry | Logs, metrics, and trace context | Component/SPI support is present | +| Database and schedule | Extended runtime APIs | Treat as alpha and verify the selected adapter | -By developing an adaptation layer project (providing the same annotation/interface call method), the implementation of the original middleware API is changed to Capa API. +An API contract being present does not guarantee that every SPI adapter implements every operation. Check the adapter repository and run integration tests against the target infrastructure before production use. -In this way, the application only needs to change a few code (such as changing the path name of the annotation/interface) to migrate to the Capa architecture. +## Repository layout -For discussion of this issue, please see: +```text +. +├── sdk/ # Public Capa SDK +├── sdk-component/ # Component discovery and shared implementations +├── sdk-spi/ # SPI extension points +├── sdk-spi-demo/ # Demo SPI implementation +├── sdk-infrastructure/ # Runtime infrastructure and hooks +├── sdk-springboot/ # Spring Boot integration +├── examples/ # Example applications +└── spec/ # Protocol definitions +``` -[Java sdk design 调研:能否复用业界已有的事实标准](https://github.com/mosn/layotto/issues/206) +The design follows **standard APIs plus replaceable SDK components**: -[Capa API adapted to spring annotation.](https://github.com/reactivegroup/sigs/issues/16) +

+ Capa SDK layers +

-[遗留中间件SDK无感迁移到Capa.](https://github.com/reactivegroup/sigs/issues/18) +## Build and verify -## Develop +```bash +git clone https://github.com/capa-cloud/capa-java.git +cd capa-java +mvn --batch-mode --no-transfer-progress --fail-fast clean verify \ + -Pjacoco,rat,checkstyle \ + -DskipTests=false \ + -Dcheckstyle.skip=false \ + -Drat.skip=false \ + -Dmaven.javadoc.skip=true \ + -Dgpg.skip=true +``` -#### Reactor API +This is the same verification profile used by CI on Java 8 and Java 11. -Taking into account the asynchronous call mode and the use of non-blocking IO, we provide the Reactor programming model natively. You can also use the synchronous call function through its `block()` method. +Runnable and configuration examples are available in [`examples/`](examples/) and [`sdk-spi-demo/`](sdk-spi-demo/). Sample component mappings live under each module's `src/main/resources/sample/` directory. -The Java SDK for Capa is built using [Project Reactor](https://projectreactor.io/). It provides an asynchronous API for -Java. When consuming a result is consumed synchronously, as in the examples referenced above, the `block()` method is -used. +## Reactive API model -The code below does not make any API call, it simply returns -the [Mono](https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Mono.html) publisher object. Nothing -happens until the application subscribes or blocks on the result: +Capa uses [Project Reactor](https://github.com/reactor/reactor-core) for asynchronous operations. A returned [`Mono`](https://github.com/reactor/reactor-core/blob/main/docs/modules/ROOT/pages/coreFeatures/mono.adoc) remains lazy until it is subscribed to or blocked: ```java -Mono result=capaRpcClient.invokeMethod(SERVICE_APP_ID,"say","hello",HttpExtension.POST,null,TypeRef.STRING); +Mono result = capaRpcClient.invokeMethod( + SERVICE_APP_ID, + "say", + "hello", + HttpExtension.POST, + null, + TypeRef.STRING +); + +String response = result.block(); ``` -To start execution and receive the result object synchronously, use `block()`. The code below shows how to execute the -call and consume an empty response: +Use non-blocking composition in reactive applications. Call `block()` only at an intentional synchronous boundary. -```java -Mono result=capaRpcClient.invokeMethod(SERVICE_APP_ID,"say","hello",HttpExtension.POST,null,TypeRef.STRING); - String response=result.block(); -``` +## Design background + +- [Dapr API future plans](https://github.com/dapr/dapr/issues/2817) +- [Layotto Java SDK design discussion](https://github.com/mosn/layotto/issues/188) +- [Adapting Capa APIs to Spring annotations](https://github.com/reactivegroup/sigs/issues/16) +- [Migrating legacy middleware SDKs to Capa](https://github.com/reactivegroup/sigs/issues/18) +- [Multi-Runtime 2022: open questions](https://zhuanlan.zhihu.com/p/435012312) -#### Exception handling +## Contributing -Most exceptions thrown from the SDK are instances of `CapaException`. `CapaException` extends from `RuntimeException`, -making it compatible with Project Reactor. +1. Create a branch from `master`. +2. Keep public API changes compatible or document the compatibility impact. +3. Update examples whenever an SDK or SPI contract changes. +4. Run the full verification command above. +5. Open a pull request with the affected capability and adapter scope. -## Future +Do not commit cloud credentials, private endpoints, or customer configuration in examples or test resources. -### Multi-Runtime +## License -[Multi-Runtime 2022:待解决的问题](https://zhuanlan.zhihu.com/p/435012312?utm_source=wechat_session&utm_medium=social&utm_oi=618742049890111488&utm_content=group2_article&utm_campaign=shareopn) +Apache License 2.0. See [LICENSE](LICENSE). diff --git a/README_ZH.md b/README_ZH.md index a13a2ae..59df050 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -1,210 +1,153 @@ -![logo](./docs/logo.png) +

+ Capa +

-# Capa(cloud application api): To be the high-level api layer for all application runtime. +# Capa Java SDK -让代码实现"一次编写,随处运行"。 +Capa 是面向 Java 应用的富 SDK 模式 Cloud Application API 实现。应用依赖与厂商无关的运行时接口,通过可替换的 SPI 模块连接具体中间件或云服务。 -借助Capa体系,使你的Java应用在改动量较小的情况下,拥有跨云、混合云运行的能力。 +[English](README.md) · [在线文档](https://capa.rxcloud.group/) · [问题反馈](https://github.com/capa-cloud/capa-java/issues) -## 动机 +> **项目边界:**本仓库是 Java SDK。实验性的 Go Sidecar 运行时位于 [capa](https://github.com/capa-cloud/capa),API 契约位于 [cloud-runtimes-jvm](https://github.com/capa-cloud/cloud-runtimes-jvm)。 -### Mecha架构 +## 环境要求 -Capa项目基于Mecha架构的设计理念,使用 **富SDK模式** 提供Multi-Runtime的标准API。 +- Java 8 或 Java 11 +- Maven 3.8.1 或更高版本 +- 为应用实际使用的每项能力提供对应的 Capa SPI 实现 -您可以简单的将Capa项目理解为 [Dapr](https://github.com/dapr/dapr) / [Layotto](https://github.com/mosn/layotto) 等Sidecar模式项目的SDK实现版本。 +### 日志依赖兼容性 -欲要理解Mecha架构的设计思路,请阅读以下文章: +兼容 Java 8 的构建固定使用 Logback `1.3.16`。Logback 上游已经将整个 +1.3.x 系列标记为停止维护,仍受维护的 1.5.x 系列要求 Java 11 或更高版本。 +使用 Capa Logback 集成的应用应选择以下路径之一: -[死生之地不可不察:论API标准化对Dapr的重要性](https://www.infoq.cn/article/wjkNGoGaaHyKs7xIyTSB) +- Java 11 或更高版本:通过依赖管理将 `logback-core` 和 + `logback-classic` 更新到 `1.5.38` 或之后兼容的 1.5.x 版本。CI 使用 + `-Dlogback.version=1.5.38` 验证该组合。 +- Java 8:尽可能使用仍在维护的 Log4j 2 集成;如果应用会接收不可信的日志 + 配置,应先迁移到 Java 11。 -[MOSN子项目Layotto:开启服务网格+应用运行时新篇章](http://mosn.io/layotto/#/zh/blog/mosn-subproject-layotto-opening-a-new-chapter-in-service-grid-application-runtime/index) +当前运行时要求以 [Logback 下载与支持状态](https://logback.qos.ch/download.html) +为准。不要从不可信来源加载日志配置。 -### Sidecar or SDK +## 引入 SDK -基于Mecha架构理念的Multi-Runtime,以Sidecar的方式提供标准API的功能,看起来似乎是最合理的选择。 - -那为什么不直接使用Dapr/Layotto等项目,而是选择开发 **富SDK模式** 的Capa项目呢。 - -概括:_以Dapr为代表的Sidecar架构是未来,但现有的很多企业和系统很难一步到位的升级到Sidecar架构,富SDK架构将会长期的存在下去。_ - -引申:_面对庞大的Java系统体系,Capa项目将使用富SDK模型支持Java系统向Mecha架构过渡。在Dapr等项目成熟后,也可以无缝衔接到Sidecar架构。_ - -关于此问题的具体讨论请参考: - -[SDK模型的Dapr API](https://github.com/dapr/dapr/issues/3261) - -[Dapr API的未来计划](https://github.com/dapr/dapr/issues/2817) - -[Java SDK的设计讨论](https://github.com/mosn/layotto/issues/188) - -## 特征 - -### API定义 - -Capa API设计follow社区标准,请参考 Dapr / Layotto 等开源项目的API定义。 - -API定义放置于以下独立仓库中,与Capa项目解绑,希望发展成为社区的API标准定义: - -+ java: [cloud-runtimes-jvm](https://github.com/reactivegroup/cloud-runtimes-jvm) -+ python(alpha): [cloud-runtimes-python](https://github.com/reactivegroup/cloud-runtimes-python) -+ golang(alpha): [cloud-runtimes-golang](https://github.com/reactivegroup/cloud-runtimes-golang) - -#### 为什么不直接使用Dapr API? - -由于目前Dapr API和Dapr项目强绑定,但我们希望这套API能够成为整个社区的标准,所以Capa将API定义放在独立仓库中,并时刻和上游社区标准保持同步。 - -我们希望后续Dapr能够将其API独立部署出来,与Dapr项目相解耦,成为整个社区的标准。 - -关于此项的讨论,请查看: - -[Future plans for dapr api](https://github.com/dapr/dapr/issues/2817) - -### Capa特性 - -Capa(Java SDK)是面向Java应用实现Mecha架构的SDK解决方案,它目前支持以下领域的特性: - -+ Service Invocation (RPC服务调用) -+ Configuration Centor (Configuration动态配置) -+ Publish/Subscribe (Pub/Sub发布订阅) -+ State Management (State状态管理) -+ Application Log/Metrics/Traces (Telemetry可观测性) -+ Database (SQL关系型数据库) -alpha -+ Schedule (Schedule定时调度) -alpha -+ ... - -## 设计 - -### Capa设计 - -设计思路:**标准API + 可拔插可替换的SDK组件** 模式 - -在不同的分布式中间件领域,Capa提供与具体中间件API无关的统一的标准编程API。 所以应用程序在使用Capa编程时不需要依赖任何具体的中间件API,只需要依赖Capa的标准编程API即可。 - -在部署到不同的目标环境时,Capa将会装载标准API的不同实现类到应用程序中。当调用统一的编程API时,底层运行时会适配到不同的具体中间件SDK实现。 - -中间件团队需要针对不同目标环境,开发标准API在目标环境下的实现类即可;而应用代码可以拥有"一次编写,随处运行"的开发体验。 - -### SDK设计 - -Capa module划分主要为以下几个部分: - -* sdk -* sdk-component -* sdk-spi -* sdk-spi-demo/... - -![capa-design](./docs/capa-design/capa-layer.PNG) - -应用程序编程时只需要依赖sdk即可,并使用SDK模块中定义的统一编程API。 - -在运行前,会将具体的SPI实现包引入进去,作为统一编程API的具体实现。 - -## 使用 - -### Getting Started - -#### Importing Capa's Java SDK - -For a Maven project, add the following to your pom.xml file: +当前仓库版本为 `1.11.13.2.RELEASE`。 ```xml - - - ... - - ... - - - group.rxcloud - capa-sdk - 1.0.7.RELEASE - - ... - - ... - + + group.rxcloud + capa-sdk + 1.11.13.2.RELEASE + ``` -Sample implementation library: +`capa-sdk` 提供统一编程接口,但不会自行选择生产基础设施。应用还需要引入兼容的 SPI 实现,例如组织内部适配器或对应的云集成仓库。 -```xml +仅用于本地体验时,可以使用仓库提供的 Demo SPI: - - ... - - ... - - - group.rxcloud - capa-sdk-spi-demo - 1.0.7.RELEASE - - ... - - ... - +```xml + + group.rxcloud + capa-sdk-spi-demo + 1.11.13.2.RELEASE + runtime + ``` -### Running the examples - -Try the following examples to learn more about Capa's Java SDK: - -* [capa-demo](https://github.com/reactivegroup/capa/tree/master/sdk-spi-demo) -* [capa-aws](https://github.com/reactivegroup/capa-aws) -* [capa-alibaba](https://github.com/reactivegroup/capa-alibaba) +Demo SPI 是示例实现,不应作为生产运行时使用。 -### 低改造成本迁移 +## 能力范围 -如果要使用原生的Capa API,您的遗留系统需要面对较大的重构工作量。 +SDK 与 API 模块覆盖以下运行时领域: -为了使迁移做到低成本,我们可以复用目前使用到的中间件API。 +| 领域 | 用途 | 成熟度说明 | +| --- | --- | --- | +| RPC | 服务调用 | 已包含 API、组件层与 SPI 扩展点 | +| Configuration | 动态配置存储 | 已包含 API、组件层与 SPI 扩展点 | +| Pub/Sub | 消息发布与订阅 | 已包含 API、组件层与 SPI 扩展点 | +| State | 键值状态操作 | 已定义 API,具体支持取决于适配器 | +| Telemetry | 日志、指标与追踪上下文 | 已包含组件和 SPI 支持 | +| Database、Schedule | 扩展运行时 API | 按 alpha 能力对待,并验证所选适配器 | -通过开发一个适配层项目(提供相同的注解/接口调用方式),将原中间件API的实现更改为Capa API。 +API 契约存在不代表每个 SPI 适配器都实现了所有操作。用于生产环境前,应检查适配器仓库,并针对目标基础设施运行集成测试。 -如此一来,应用程序只需要更改很少的代码(例如更换注解/接口的路径名)即可迁移到Capa架构。 +## 仓库结构 -关于该问题的讨论,请查看: +```text +. +├── sdk/ # Capa 公共 SDK +├── sdk-component/ # 组件发现与共享实现 +├── sdk-spi/ # SPI 扩展点 +├── sdk-spi-demo/ # Demo SPI 实现 +├── sdk-infrastructure/ # 运行时基础设施和钩子 +├── sdk-springboot/ # Spring Boot 集成 +├── examples/ # 示例应用 +└── spec/ # 协议定义 +``` -[Java sdk design 调研:能否复用业界已有的事实标准](https://github.com/mosn/layotto/issues/206) +整体设计为“**标准 API + 可替换的 SDK 组件**”: -[Capa API adapted to spring annotation.](https://github.com/reactivegroup/sigs/issues/16) +

+ Capa SDK 分层 +

-[遗留中间件SDK无感迁移到Capa.](https://github.com/reactivegroup/sigs/issues/18) +## 构建与验证 -## 开发 +```bash +git clone https://github.com/capa-cloud/capa-java.git +cd capa-java +mvn --batch-mode --no-transfer-progress --fail-fast clean verify \ + -Pjacoco,rat,checkstyle \ + -DskipTests=false \ + -Dcheckstyle.skip=false \ + -Drat.skip=false \ + -Dmaven.javadoc.skip=true \ + -Dgpg.skip=true +``` -#### Reactor API +CI 使用同一套命令分别在 Java 8 和 Java 11 上验证。 -考虑到异步调用模式,以及非阻塞IO的使用,我们原生提供Reactor的编程模型,您也可以通过其`block()`方法使用同步调用的功能。 +可运行示例与配置示例位于 [`examples/`](examples/) 和 [`sdk-spi-demo/`](sdk-spi-demo/)。各模块的 `src/main/resources/sample/` 目录包含组件映射样例。 -The Java SDK for Capa is built using [Project Reactor](https://projectreactor.io/). It provides an asynchronous API for -Java. When consuming a result is consumed synchronously, as in the examples referenced above, the `block()` method is -used. +## 响应式 API 模型 -The code below does not make any API call, it simply returns -the [Mono](https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Mono.html) publisher object. Nothing -happens until the application subscribes or blocks on the result: +Capa 使用 [Project Reactor](https://github.com/reactor/reactor-core) 提供异步操作。返回的 [`Mono`](https://github.com/reactor/reactor-core/blob/main/docs/modules/ROOT/pages/coreFeatures/mono.adoc) 在订阅或阻塞前不会执行: ```java -Mono result=capaRpcClient.invokeMethod(SERVICE_APP_ID,"say","hello",HttpExtension.POST,null,TypeRef.STRING); +Mono result = capaRpcClient.invokeMethod( + SERVICE_APP_ID, + "say", + "hello", + HttpExtension.POST, + null, + TypeRef.STRING +); + +String response = result.block(); ``` -To start execution and receive the result object synchronously, use `block()`. The code below shows how to execute the -call and consume an empty response: +响应式应用应优先使用非阻塞组合;只有在明确的同步边界才调用 `block()`。 -```java -Mono result=capaRpcClient.invokeMethod(SERVICE_APP_ID,"say","hello",HttpExtension.POST,null,TypeRef.STRING); - String response=result.block(); -``` +## 设计背景 + +- [Dapr API 后续规划](https://github.com/dapr/dapr/issues/2817) +- [Layotto Java SDK 设计讨论](https://github.com/mosn/layotto/issues/188) +- [将 Capa API 适配到 Spring 注解](https://github.com/reactivegroup/sigs/issues/16) +- [将遗留中间件 SDK 迁移到 Capa](https://github.com/reactivegroup/sigs/issues/18) +- [Multi-Runtime 2022:待解决的问题](https://zhuanlan.zhihu.com/p/435012312) -#### Exception handling +## 参与贡献 -Most exceptions thrown from the SDK are instances of `CapaException`. `CapaException` extends from `RuntimeException`, -making it compatible with Project Reactor. +1. 从 `master` 创建分支。 +2. 公共 API 变更需要保持兼容,或明确说明兼容性影响。 +3. SDK 或 SPI 契约变化时同步更新示例。 +4. 运行上面的完整验证命令。 +5. 在 Pull Request 中说明受影响的能力和适配器范围。 -## 未来发展 +不要在示例或测试资源中提交云凭证、私有端点或客户配置。 -### 关于Multi-Runtime的思考 +## 许可证 -[Multi-Runtime 2022:待解决的问题](https://zhuanlan.zhihu.com/p/435012312?utm_source=wechat_session&utm_medium=social&utm_oi=618742049890111488&utm_content=group2_article&utm_campaign=shareopn) +Apache License 2.0,详见 [LICENSE](LICENSE)。 diff --git a/docs/capa-architecture.png b/docs/capa-architecture.png new file mode 100644 index 0000000..e25105d Binary files /dev/null and b/docs/capa-architecture.png differ diff --git a/docs/capa-features.png b/docs/capa-features.png new file mode 100644 index 0000000..0e646b8 Binary files /dev/null and b/docs/capa-features.png differ diff --git a/docs/capa-write-once-run-anywhere.png b/docs/capa-write-once-run-anywhere.png new file mode 100644 index 0000000..c9c2490 Binary files /dev/null and b/docs/capa-write-once-run-anywhere.png differ diff --git a/examples/pom.xml b/examples/pom.xml index 259e7d8..e681f33 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -23,7 +23,7 @@ capa-parent group.rxcloud - 1.10.10.RELEASE + 1.11.13.2.RELEASE capa-examples @@ -31,9 +31,9 @@ capa-sdk-examples - 2.14.1 - 1.2.3 - 1.7.32 + 2.25.4 + 1.3.16 + 2.0.17 @@ -72,12 +72,7 @@ logback-classic ${logback.version} - - - org.projectlombok - lombok - 1.18.2 - + @@ -117,4 +112,4 @@ - \ No newline at end of file + diff --git a/examples/src/main/java/group/rxcloud/capa/examples/log/DemoLog.java b/examples/src/main/java/group/rxcloud/capa/examples/log/DemoLog.java index b64ac9d..867aacd 100644 --- a/examples/src/main/java/group/rxcloud/capa/examples/log/DemoLog.java +++ b/examples/src/main/java/group/rxcloud/capa/examples/log/DemoLog.java @@ -16,7 +16,8 @@ */ package group.rxcloud.capa.examples.log; -import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * An application cannot use log4j and logback configuration to print logs at the same time. @@ -26,13 +27,14 @@ * 1. Resources cannot contain log4j2.xml and logback.xml files at the same time, * 2. log4j-slf4j-impl and logback-classic cannot exist at the same time. */ -@Slf4j public class DemoLog { + private static final Logger LOG = LoggerFactory.getLogger(DemoLog.class); + public static void main(String[] args) { try { - log.info("test"); - }catch (Exception e){ + LOG.info("test"); + } catch (Exception e) { System.out.println(); } diff --git a/examples/src/main/java/group/rxcloud/capa/examples/telemetry/MetricTestExporter.java b/examples/src/main/java/group/rxcloud/capa/examples/telemetry/MetricTestExporter.java index 2615920..2cb0a93 100644 --- a/examples/src/main/java/group/rxcloud/capa/examples/telemetry/MetricTestExporter.java +++ b/examples/src/main/java/group/rxcloud/capa/examples/telemetry/MetricTestExporter.java @@ -17,6 +17,8 @@ package group.rxcloud.capa.examples.telemetry; import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.metrics.InstrumentType; +import io.opentelemetry.sdk.metrics.data.AggregationTemporality; import io.opentelemetry.sdk.metrics.data.MetricData; import io.opentelemetry.sdk.metrics.export.MetricExporter; @@ -35,6 +37,11 @@ public CompletableResultCode flush() { return CompletableResultCode.ofSuccess(); } + @Override + public AggregationTemporality getAggregationTemporality(InstrumentType instrumentType) { + return AggregationTemporality.CUMULATIVE; + } + @Override public CompletableResultCode shutdown() { return CompletableResultCode.ofSuccess(); diff --git a/pom.xml b/pom.xml index 6d5a710..67e3649 100644 --- a/pom.xml +++ b/pom.xml @@ -23,10 +23,10 @@ group.rxcloud capa-parent pom - 1.10.10.RELEASE + 1.11.13.2.RELEASE capa-sdk-parent SDK for Capa. - https://github.com/reactivegroup + https://github.com/capa-cloud/capa-java @@ -51,9 +51,9 @@ - scm:git:git@github.com:reactivegroup/capa.git - scm:git:git@github.com:reactivegroup/capa.git - git@github.com:reactivegroup/capa.git + scm:git:https://github.com/capa-cloud/capa-java.git + scm:git:git@github.com:capa-cloud/capa-java.git + https://github.com/capa-cloud/capa-java @@ -81,13 +81,12 @@ 8 UTF-8 3.8.1 - 1.0.10.RELEASE + 1.11.RELEASE 3.3.22.RELEASE 1.7.21 1.39.0 - 2.12.4 - 1.9.0 - 1.9.0-alpha + 2.18.9 + 1.62.0 5.3.1 3.6.0 2.0.2 @@ -97,7 +96,7 @@ 2.7 3.2.0 3.0.0-M1 - 0.8.6 + 0.8.15 true true true @@ -137,6 +136,12 @@ group.rxcloud cloud-runtimes-api ${cloud-runtimes.version} + + + io.opentelemetry + opentelemetry-api-metrics + + @@ -173,11 +178,6 @@ opentelemetry-api ${open.telemetry.version} - - io.opentelemetry - opentelemetry-api-metrics - ${open.telemetry.version.alpha} - @@ -440,4 +440,5 @@ - \ No newline at end of file + + diff --git a/sdk-component/pom.xml b/sdk-component/pom.xml index adf9c8c..bfd3dd2 100644 --- a/sdk-component/pom.xml +++ b/sdk-component/pom.xml @@ -23,7 +23,7 @@ group.rxcloud capa-parent - 1.10.10.RELEASE + 1.11.13.2.RELEASE capa-sdk-component @@ -31,10 +31,11 @@ SDK Component for Capa - 4.9.1 - 1.4.10 - 2.14.1 - 1.2.3 + 4.12.0 + 1.8.21 + 2.25.4 + 1.3.16 + 2.0.17 @@ -53,7 +54,7 @@ io.opentelemetry opentelemetry-sdk-metrics - ${open.telemetry.version.alpha} + ${open.telemetry.version} @@ -170,4 +171,4 @@ - \ No newline at end of file + diff --git a/sdk-component/src/main/java/group/rxcloud/capa/component/CapaRpcProperties.java b/sdk-component/src/main/java/group/rxcloud/capa/component/CapaRpcProperties.java new file mode 100644 index 0000000..9e1e1d8 --- /dev/null +++ b/sdk-component/src/main/java/group/rxcloud/capa/component/CapaRpcProperties.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package group.rxcloud.capa.component; + +import group.rxcloud.capa.infrastructure.CapaProperties; + +import java.util.Properties; + +/** + * Capa rpc component common properties. + */ +public interface CapaRpcProperties { + + abstract class Settings { + + /** + * Determines if Capa client will use HTTP or Other client. + */ + public static String API_PROTOCOL; + + /** + * Capa's timeout in seconds for HTTP client reads. + */ + public static Integer HTTP_CLIENT_READ_TIMEOUT_SECONDS; + + /** + * Capa's default use of HTTP. + */ + private static final String DEFAULT_API_PROTOCOL = "HTTP"; + + /** + * Capa's default timeout in seconds for HTTP client reads. + */ + private static final Integer DEFAULT_HTTP_CLIENT_READTIMEOUTSECONDS = 60; + + static { + Properties properties = CapaProperties.COMPONENT_PROPERTIES_SUPPLIER.apply("rpc"); + + API_PROTOCOL = properties.getProperty("API_PROTOCOL", DEFAULT_API_PROTOCOL); + + String httpClientReadTimeoutSeconds = properties.getProperty("HTTP_CLIENT_READ_TIMEOUT_SECONDS", String.valueOf(DEFAULT_HTTP_CLIENT_READTIMEOUTSECONDS)); + HTTP_CLIENT_READ_TIMEOUT_SECONDS = Integer.valueOf(httpClientReadTimeoutSeconds); + } + + public static String getApiProtocol() { + return API_PROTOCOL; + } + + public static Integer getHttpClientReadTimeoutSeconds() { + return HTTP_CLIENT_READ_TIMEOUT_SECONDS; + } + + private Settings() { + } + } +} diff --git a/sdk-component/src/main/java/group/rxcloud/capa/component/http/CapaHttp.java b/sdk-component/src/main/java/group/rxcloud/capa/component/http/CapaHttp.java index f43a0aa..d9b2224 100644 --- a/sdk-component/src/main/java/group/rxcloud/capa/component/http/CapaHttp.java +++ b/sdk-component/src/main/java/group/rxcloud/capa/component/http/CapaHttp.java @@ -107,6 +107,7 @@ public Mono> invokeApi(String httpMethod, /** * Invokes an API that returns a {@code } payload. + * FIXME: expose {@code Mono}, not {@code CompletableFuture} * * @param The Type of the return. * @param httpMethod HTTP httpMethod. diff --git a/sdk-component/src/main/java/group/rxcloud/capa/component/http/CapaHttpBuilder.java b/sdk-component/src/main/java/group/rxcloud/capa/component/http/CapaHttpBuilder.java index 087ceba..2aca826 100644 --- a/sdk-component/src/main/java/group/rxcloud/capa/component/http/CapaHttpBuilder.java +++ b/sdk-component/src/main/java/group/rxcloud/capa/component/http/CapaHttpBuilder.java @@ -17,8 +17,8 @@ package group.rxcloud.capa.component.http; +import group.rxcloud.capa.component.CapaRpcProperties; import group.rxcloud.capa.infrastructure.CapaClassLoader; -import group.rxcloud.capa.infrastructure.CapaProperties; import group.rxcloud.capa.infrastructure.serializer.CapaObjectSerializer; import group.rxcloud.capa.infrastructure.serializer.DefaultObjectSerializer; import okhttp3.OkHttpClient; @@ -96,7 +96,7 @@ private CapaHttp buildCapaHttp() { if (OK_HTTP_CLIENT.get() == null) { OkHttpClient.Builder builder = new OkHttpClient.Builder(); // read timeout property - Duration readTimeout = Duration.ofSeconds(CapaProperties.HTTP_CLIENT_READ_TIMEOUT_SECONDS.get()); + Duration readTimeout = Duration.ofSeconds(CapaRpcProperties.Settings.getHttpClientReadTimeoutSeconds()); builder.readTimeout(readTimeout); OkHttpClient okHttpClient = builder.build(); OK_HTTP_CLIENT.set(okHttpClient); diff --git a/sdk-component/src/main/java/group/rxcloud/capa/component/log/CapaLogbackAppenderAgent.java b/sdk-component/src/main/java/group/rxcloud/capa/component/log/CapaLogbackAppenderAgent.java index b84ffdc..bad5fd4 100644 --- a/sdk-component/src/main/java/group/rxcloud/capa/component/log/CapaLogbackAppenderAgent.java +++ b/sdk-component/src/main/java/group/rxcloud/capa/component/log/CapaLogbackAppenderAgent.java @@ -24,6 +24,7 @@ * The agent of the logback impl. */ public class CapaLogbackAppenderAgent extends UnsynchronizedAppenderBase { + /** * The log component type. */ diff --git a/sdk-component/src/main/java/group/rxcloud/capa/component/telemetry/SamplerConfig.java b/sdk-component/src/main/java/group/rxcloud/capa/component/telemetry/SamplerConfig.java index def1b85..60df1ac 100644 --- a/sdk-component/src/main/java/group/rxcloud/capa/component/telemetry/SamplerConfig.java +++ b/sdk-component/src/main/java/group/rxcloud/capa/component/telemetry/SamplerConfig.java @@ -16,13 +16,6 @@ */ package group.rxcloud.capa.component.telemetry; -import group.rxcloud.capa.component.CapaTelemetryProperties; -import group.rxcloud.capa.component.telemetry.metrics.CapaMeterProviderBuilder; -import group.rxcloud.capa.infrastructure.hook.MergedPropertiesConfig; -import group.rxcloud.capa.infrastructure.hook.Mixer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.Serializable; import java.util.function.Supplier; @@ -39,54 +32,14 @@ public class SamplerConfig implements Serializable { setMetricsEnable(true); }}; - public static final transient SamplerConfig CONFIG = new SamplerConfig(); - - private static final long serialVersionUID = -2113523925814197551L; - - private static final transient Logger log = LoggerFactory.getLogger(CapaMeterProviderBuilder.class); - - public static transient Supplier DEFAULT_SUPPLIER = () -> CONFIG; - - static { - Mixer.configurationHooksNullable().ifPresent(hooks -> { - String fileName = "capa-component-telemetry-sample.properties"; - try { - // TODO: 2021/12/3 Move this to SPI module. - // TODO: 2021/12/3 And use Configuration extension api to get merged file. - MergedPropertiesConfig config = new MergedPropertiesConfig( - fileName, - hooks.defaultConfigurationAppId(), - CapaTelemetryProperties.Settings.getCenterConfigAppId()); - String metricKey = "metricsEnable"; - String traceKey = "traceEnable"; - SamplerConfig dynamicConfig = new SamplerConfig() { - @Override - public Boolean isMetricsEnable() { - return !config.containsKey(metricKey) || Boolean.TRUE.toString() - .equalsIgnoreCase(config.get(metricKey)); - } - - @Override - public Boolean isTraceEnable() { - return !config.containsKey(traceKey) || Boolean.TRUE.toString() - .equalsIgnoreCase(config.get(traceKey)); - } - }; - - DEFAULT_SUPPLIER = () -> dynamicConfig; - } catch (Throwable throwable) { - log.warn("Fail to load global telemetry config. Dynamic global config is disabled for capa telemetry.", - throwable); - } - }); - } + public static final transient Supplier DEFAULT_SUPPLIER = () -> DEFAULT_CONFIG; private Boolean metricsEnable; private Boolean traceEnable; public Boolean isMetricsEnable() { - return metricsEnable == null ? DEFAULT_CONFIG.metricsEnable : metricsEnable; + return metricsEnable == null ? DEFAULT_SUPPLIER.get().metricsEnable : metricsEnable; } public void setMetricsEnable(boolean metricsEnable) { @@ -94,7 +47,7 @@ public void setMetricsEnable(boolean metricsEnable) { } public Boolean isTraceEnable() { - return traceEnable == null ? DEFAULT_CONFIG.traceEnable : traceEnable; + return traceEnable == null ? DEFAULT_SUPPLIER.get().traceEnable : traceEnable; } public void setTraceEnable(boolean traceEnable) { diff --git a/sdk-component/src/main/java/group/rxcloud/capa/component/telemetry/context/CapaContextPropagatorBuilder.java b/sdk-component/src/main/java/group/rxcloud/capa/component/telemetry/context/CapaContextPropagatorBuilder.java index 0f19fc0..c364272 100644 --- a/sdk-component/src/main/java/group/rxcloud/capa/component/telemetry/context/CapaContextPropagatorBuilder.java +++ b/sdk-component/src/main/java/group/rxcloud/capa/component/telemetry/context/CapaContextPropagatorBuilder.java @@ -61,28 +61,33 @@ public CapaContextPropagatorBuilder addContextPropagators(TextMapPropagator proc * @return context propagators. */ public ContextPropagators buildContextPropagators() { - if (contextPropagatorsInstance != null && !contextPropagatorsInstance.isEmpty()) { - return ContextPropagators - .create(TextMapPropagator.composite(contextPropagatorsInstance.toArray(new TextMapPropagator[0]))); - } - + List propagators = new ArrayList<>(); initContextConfig(); if (contextConfig != null) { List types = contextConfig.getContextPropagators(); if (types != null && !types.isEmpty()) { - return ContextPropagators - .create(TextMapPropagator.composite(types.stream() - .map(path -> SpiUtils - .newInstanceWithConstructorCache(path, TextMapPropagator.class)) - .toArray(TextMapPropagator[]::new))); + types.stream() + .map(path -> SpiUtils.newInstanceWithConstructorCache(path, TextMapPropagator.class)) + .forEach(propagator -> propagators.add(propagator)); } } + if (contextPropagatorsInstance != null && !contextPropagatorsInstance.isEmpty()) { + propagators.addAll(contextPropagatorsInstance); + } + ContextPropagatorLoader loader = SpiUtils.loadFromSpiComponentFileNullable(ContextPropagatorLoader.class, "telemetry"); - if (loader == null) { - loader = ContextPropagatorLoader.DEFAULT; + if (loader != null) { + List loaded = loader.load(); + if (loaded != null) { + propagators.addAll(loaded); + } + } + + if (propagators.isEmpty()) { + return ContextPropagators.noop(); } - return loader.load(); + return ContextPropagators.create(TextMapPropagator.composite(propagators.toArray(new TextMapPropagator[0]))); } private void initContextConfig() { diff --git a/sdk-component/src/main/java/group/rxcloud/capa/component/telemetry/context/ContextPropagatorLoader.java b/sdk-component/src/main/java/group/rxcloud/capa/component/telemetry/context/ContextPropagatorLoader.java index dff7460..ab69cff 100644 --- a/sdk-component/src/main/java/group/rxcloud/capa/component/telemetry/context/ContextPropagatorLoader.java +++ b/sdk-component/src/main/java/group/rxcloud/capa/component/telemetry/context/ContextPropagatorLoader.java @@ -16,7 +16,10 @@ */ package group.rxcloud.capa.component.telemetry.context; -import io.opentelemetry.context.propagation.ContextPropagators; +import io.opentelemetry.context.propagation.TextMapPropagator; + +import java.util.Collections; +import java.util.List; /** * Load default context propagator. @@ -31,7 +34,7 @@ public interface ContextPropagatorLoader { * * @return default context propagator. */ - default ContextPropagators load() { - return ContextPropagators.noop(); + default List load() { + return Collections.emptyList(); } } diff --git a/sdk-component/src/main/java/group/rxcloud/capa/component/telemetry/metrics/CapaDoubleHistogramBuilder.java b/sdk-component/src/main/java/group/rxcloud/capa/component/telemetry/metrics/CapaDoubleHistogramBuilder.java index 6fafd87..0acc650 100644 --- a/sdk-component/src/main/java/group/rxcloud/capa/component/telemetry/metrics/CapaDoubleHistogramBuilder.java +++ b/sdk-component/src/main/java/group/rxcloud/capa/component/telemetry/metrics/CapaDoubleHistogramBuilder.java @@ -19,7 +19,7 @@ import io.opentelemetry.api.metrics.DoubleHistogram; import io.opentelemetry.api.metrics.DoubleHistogramBuilder; import io.opentelemetry.api.metrics.LongHistogramBuilder; -import io.opentelemetry.api.metrics.internal.NoopMeter; +import io.opentelemetry.api.metrics.MeterProvider; /** * @@ -69,7 +69,7 @@ public DoubleHistogram build() { .loadHistogramNullable(meterName, schemaUrl, version, name, description, unit, CapaDoubleHistogram.class); if (histogram == null) { - return NoopMeter.getInstance().histogramBuilder(name).build(); + return MeterProvider.noop().get(meterName).histogramBuilder(name).build(); } return histogram; } diff --git a/sdk-component/src/main/java/group/rxcloud/capa/component/telemetry/metrics/CapaLongHistogramBuilder.java b/sdk-component/src/main/java/group/rxcloud/capa/component/telemetry/metrics/CapaLongHistogramBuilder.java index 508c943..301326f 100644 --- a/sdk-component/src/main/java/group/rxcloud/capa/component/telemetry/metrics/CapaLongHistogramBuilder.java +++ b/sdk-component/src/main/java/group/rxcloud/capa/component/telemetry/metrics/CapaLongHistogramBuilder.java @@ -19,7 +19,7 @@ import io.opentelemetry.api.metrics.DoubleHistogramBuilder; import io.opentelemetry.api.metrics.LongHistogram; import io.opentelemetry.api.metrics.LongHistogramBuilder; -import io.opentelemetry.api.metrics.internal.NoopMeter; +import io.opentelemetry.api.metrics.MeterProvider; /** * @@ -57,7 +57,6 @@ public LongHistogramBuilder setUnit(String unit) { return this; } - @Override public DoubleHistogramBuilder ofDoubles() { return new CapaDoubleHistogramBuilder(meterName, schemaUrl, version, name).setDescription(description) .setUnit(unit); @@ -68,7 +67,7 @@ public LongHistogram build() { LongHistogram histogram = CapaMeterWrapper .loadHistogramNullable(meterName, schemaUrl, version, name, description, unit, CapaLongHistogram.class); if (histogram == null) { - return NoopMeter.getInstance().histogramBuilder(name).ofLongs().build(); + return MeterProvider.noop().get(meterName).histogramBuilder(name).ofLongs().build(); } return histogram; } diff --git a/sdk-component/src/main/java/group/rxcloud/capa/component/telemetry/metrics/CapaMeterProviderBuilder.java b/sdk-component/src/main/java/group/rxcloud/capa/component/telemetry/metrics/CapaMeterProviderBuilder.java index 160bd28..33b4be7 100644 --- a/sdk-component/src/main/java/group/rxcloud/capa/component/telemetry/metrics/CapaMeterProviderBuilder.java +++ b/sdk-component/src/main/java/group/rxcloud/capa/component/telemetry/metrics/CapaMeterProviderBuilder.java @@ -22,7 +22,7 @@ import io.opentelemetry.sdk.metrics.SdkMeterProvider; import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; import io.opentelemetry.sdk.metrics.export.MetricExporter; -import io.opentelemetry.sdk.metrics.export.MetricReaderFactory; +import io.opentelemetry.sdk.metrics.export.MetricReader; import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; import org.jetbrains.annotations.NotNull; @@ -62,9 +62,9 @@ public class CapaMeterProviderBuilder implements CapaMeterProviderSettings { * @param readerConfigs metrics reader configs. * @return metrics reader factories. */ - private static List bulidReaderFactories(List readerConfigs, - Supplier samplerConfig) { - List factories = new ArrayList<>(); + private static List buildReaders(List readerConfigs, + Supplier samplerConfig) { + List readers = new ArrayList<>(); for (MetricsReaderConfig config : readerConfigs) { MetricExporter exporter = SpiUtils .newInstance(config.getExporterType(), CapaMetricsExporter.class, new Class[]{Supplier.class}, @@ -85,12 +85,12 @@ public Thread newThread(@NotNull Runnable r) { } }); - factories.add(PeriodicMetricReader.builder(exporter) - .setInterval(config.getExportIntervalMillis(), TimeUnit.MILLISECONDS) - .setExecutor(worker) - .newMetricReaderFactory()); + readers.add(PeriodicMetricReader.builder(exporter) + .setInterval(config.getExportIntervalMillis(), TimeUnit.MILLISECONDS) + .setExecutor(worker) + .build()); } - return factories; + return readers; } @Override @@ -136,12 +136,13 @@ public MeterProvider buildMeterProvider() { return MeterProvider.noop(); } - List factories = bulidReaderFactories(metricsReaderConfigs, samplerConfig); - + List readers = buildReaders(metricsReaderConfigs, samplerConfig); SdkMeterProviderBuilder builder = SdkMeterProvider.builder() - .setExemplarFilter(new CapaMetricsSampler(samplerConfig)); - factories.forEach(f -> builder.registerMetricReader(f)); + .setExemplarFilter( + new CapaMetricsSampler(samplerConfig) + .getExemplarFilter()); + readers.forEach(builder::registerMetricReader); SdkMeterProvider provider = builder.build(); return new CapaMeterProvider(provider); } diff --git a/sdk-component/src/main/java/group/rxcloud/capa/component/telemetry/metrics/CapaMetricsExporter.java b/sdk-component/src/main/java/group/rxcloud/capa/component/telemetry/metrics/CapaMetricsExporter.java index 3ce214e..139f334 100644 --- a/sdk-component/src/main/java/group/rxcloud/capa/component/telemetry/metrics/CapaMetricsExporter.java +++ b/sdk-component/src/main/java/group/rxcloud/capa/component/telemetry/metrics/CapaMetricsExporter.java @@ -18,6 +18,8 @@ import group.rxcloud.capa.component.telemetry.SamplerConfig; import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.metrics.InstrumentType; +import io.opentelemetry.sdk.metrics.data.AggregationTemporality; import io.opentelemetry.sdk.metrics.data.MetricData; import io.opentelemetry.sdk.metrics.export.MetricExporter; @@ -55,6 +57,11 @@ public CompletableResultCode flush() { return doFlush(); } + @Override + public AggregationTemporality getAggregationTemporality(InstrumentType instrumentType) { + return AggregationTemporality.CUMULATIVE; + } + protected abstract CompletableResultCode doExport(Collection metrics); protected abstract CompletableResultCode doFlush(); diff --git a/sdk-component/src/main/java/group/rxcloud/capa/component/telemetry/metrics/CapaMetricsSampler.java b/sdk-component/src/main/java/group/rxcloud/capa/component/telemetry/metrics/CapaMetricsSampler.java index bb93b97..5178d73 100644 --- a/sdk-component/src/main/java/group/rxcloud/capa/component/telemetry/metrics/CapaMetricsSampler.java +++ b/sdk-component/src/main/java/group/rxcloud/capa/component/telemetry/metrics/CapaMetricsSampler.java @@ -19,7 +19,7 @@ import group.rxcloud.capa.component.telemetry.SamplerConfig; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.context.Context; -import io.opentelemetry.sdk.metrics.exemplar.ExemplarFilter; +import io.opentelemetry.sdk.metrics.ExemplarFilter; import java.util.function.Supplier; @@ -38,22 +38,20 @@ public CapaMetricsSampler(Supplier samplerConfigSupplier) { this.samplerConfigSupplier = samplerConfigSupplier; } - @Override public boolean shouldSampleMeasurement(long value, Attributes attributes, Context context) { - return get().shouldSampleMeasurement(value, attributes, context); + return isMetricsEnabled(); } - @Override public boolean shouldSampleMeasurement(double value, Attributes attributes, Context context) { - return get().shouldSampleMeasurement(value, attributes, context); + return isMetricsEnabled(); } - private ExemplarFilter get() { - SamplerConfig config = samplerConfigSupplier.get(); - if (config != null && !config.isMetricsEnable()) { - return ExemplarFilter.neverSample(); - } + public ExemplarFilter getExemplarFilter() { + return isMetricsEnabled() ? ExemplarFilter.alwaysOn() : ExemplarFilter.alwaysOff(); + } - return ExemplarFilter.alwaysSample(); + private boolean isMetricsEnabled() { + SamplerConfig config = samplerConfigSupplier.get(); + return config == null || config.isMetricsEnable(); } } diff --git a/sdk-component/src/test/java/group/rxcloud/capa/component/CapaRpcPropertiesTest.java b/sdk-component/src/test/java/group/rxcloud/capa/component/CapaRpcPropertiesTest.java new file mode 100644 index 0000000..9df292b --- /dev/null +++ b/sdk-component/src/test/java/group/rxcloud/capa/component/CapaRpcPropertiesTest.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package group.rxcloud.capa.component; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class CapaRpcPropertiesTest { + + @Test + public void testGetApiProtocol_Success() { + String apiProtocol = CapaRpcProperties.Settings.getApiProtocol(); + Assertions.assertEquals("HTTP", apiProtocol); + } + + @Test + public void testGetHttpClientReadTimeoutSeconds_Success() { + Integer httpClientReadTimeoutSeconds = CapaRpcProperties.Settings.getHttpClientReadTimeoutSeconds(); + Assertions.assertEquals(60, httpClientReadTimeoutSeconds.intValue()); + } +} \ No newline at end of file diff --git a/sdk-component/src/test/java/group/rxcloud/capa/component/configstore/TestObjectSerializer.java b/sdk-component/src/test/java/group/rxcloud/capa/component/configstore/TestObjectSerializer.java index e8cf351..2425990 100644 --- a/sdk-component/src/test/java/group/rxcloud/capa/component/configstore/TestObjectSerializer.java +++ b/sdk-component/src/test/java/group/rxcloud/capa/component/configstore/TestObjectSerializer.java @@ -17,12 +17,12 @@ package group.rxcloud.capa.component.configstore; import group.rxcloud.capa.infrastructure.serializer.CapaObjectSerializer; -import group.rxcloud.capa.infrastructure.serializer.ObjectSerializer; +import group.rxcloud.capa.infrastructure.serializer.ExtensionObjectSerializer; /** * serializer/deserializer for request/response objects used in tests only */ -public class TestObjectSerializer extends ObjectSerializer implements CapaObjectSerializer { +public class TestObjectSerializer extends ExtensionObjectSerializer implements CapaObjectSerializer { /** * {@inheritDoc} diff --git a/sdk-component/src/test/java/group/rxcloud/capa/component/http/CapaHttpBuilderTest.java b/sdk-component/src/test/java/group/rxcloud/capa/component/http/CapaHttpBuilderTest.java index b1beb44..5f21200 100644 --- a/sdk-component/src/test/java/group/rxcloud/capa/component/http/CapaHttpBuilderTest.java +++ b/sdk-component/src/test/java/group/rxcloud/capa/component/http/CapaHttpBuilderTest.java @@ -18,7 +18,7 @@ import group.rxcloud.capa.infrastructure.serializer.CapaObjectSerializer; import group.rxcloud.capa.infrastructure.serializer.DefaultObjectSerializer; -import group.rxcloud.capa.infrastructure.serializer.ObjectSerializer; +import group.rxcloud.capa.infrastructure.serializer.ExtensionObjectSerializer; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -53,7 +53,7 @@ public void testBuild_Success() { /** * serializer/deserializer for request/response objects used in tests only */ - private class TestObjectSerializer extends ObjectSerializer implements CapaObjectSerializer { + private class TestObjectSerializer extends ExtensionObjectSerializer implements CapaObjectSerializer { /** * {@inheritDoc} diff --git a/sdk-component/src/test/java/group/rxcloud/capa/component/telemetry/SamplerConfigTest.java b/sdk-component/src/test/java/group/rxcloud/capa/component/telemetry/SamplerConfigTest.java index c8c4a59..b36b505 100644 --- a/sdk-component/src/test/java/group/rxcloud/capa/component/telemetry/SamplerConfigTest.java +++ b/sdk-component/src/test/java/group/rxcloud/capa/component/telemetry/SamplerConfigTest.java @@ -18,21 +18,33 @@ import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author: chenyijiang * @date: 2021/12/2 12:33 */ + public class SamplerConfigTest { @Test public void isMetricsEnable() { - assertFalse(SamplerConfig.DEFAULT_SUPPLIER.get().isMetricsEnable()); + assertTrue(SamplerConfig.DEFAULT_SUPPLIER.get().isMetricsEnable()); } @Test public void isTraceEnable() { - assertFalse(SamplerConfig.DEFAULT_SUPPLIER.get().isTraceEnable()); + assertTrue(SamplerConfig.DEFAULT_SUPPLIER.get().isTraceEnable()); + } + + @Test + public void configurationWeakDependency() { + for (int i = 0; i < 6; i++) { + SamplerConfig config = SamplerConfig.DEFAULT_SUPPLIER.get(); + assertNotNull(config); + assertEquals(SamplerConfig.DEFAULT_CONFIG, config); + } } } \ No newline at end of file diff --git a/sdk-component/src/test/java/group/rxcloud/capa/component/telemetry/context/CapaContextPropagatorBuilderTest.java b/sdk-component/src/test/java/group/rxcloud/capa/component/telemetry/context/CapaContextPropagatorBuilderTest.java index 8256824..fd852a2 100644 --- a/sdk-component/src/test/java/group/rxcloud/capa/component/telemetry/context/CapaContextPropagatorBuilderTest.java +++ b/sdk-component/src/test/java/group/rxcloud/capa/component/telemetry/context/CapaContextPropagatorBuilderTest.java @@ -86,7 +86,7 @@ public void set(@Nullable Object carrier, String key, String value) { } }); - assertEquals(2, TestPropagator.getCalled()); + assertEquals(5, TestPropagator.getCalled()); } diff --git a/sdk-component/src/test/java/group/rxcloud/capa/component/telemetry/metrics/CapaDoubleHistogramBuilderTest.java b/sdk-component/src/test/java/group/rxcloud/capa/component/telemetry/metrics/CapaDoubleHistogramBuilderTest.java index d0796b5..20d738e 100644 --- a/sdk-component/src/test/java/group/rxcloud/capa/component/telemetry/metrics/CapaDoubleHistogramBuilderTest.java +++ b/sdk-component/src/test/java/group/rxcloud/capa/component/telemetry/metrics/CapaDoubleHistogramBuilderTest.java @@ -17,8 +17,7 @@ package group.rxcloud.capa.component.telemetry.metrics; import io.opentelemetry.api.metrics.DoubleHistogramBuilder; -import io.opentelemetry.api.metrics.LongHistogramBuilder; -import io.opentelemetry.api.metrics.internal.NoopMeter; +import io.opentelemetry.api.metrics.MeterProvider; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; @@ -34,9 +33,10 @@ public void build() { CapaDoubleHistogramBuilder builder = new CapaDoubleHistogramBuilder("", "", "", ""); assertEquals(builder, builder.setDescription("aaa")); assertEquals(builder, builder.setUnit("bbb")); - assertTrue(builder.build() instanceof NoopMeter.NoopDoubleHistogram); + assertEquals(MeterProvider.noop().get("test").histogramBuilder("test").build().getClass(), + builder.build().getClass()); - LongHistogramBuilder longHistogramBuilder = builder.ofLongs(); + CapaLongHistogramBuilder longHistogramBuilder = (CapaLongHistogramBuilder) builder.ofLongs(); assertEquals(longHistogramBuilder, longHistogramBuilder.setDescription("ccc")); assertEquals(longHistogramBuilder, longHistogramBuilder.setUnit("ddd")); assertTrue(longHistogramBuilder.build() instanceof TestLongHistogram); @@ -44,4 +44,4 @@ public void build() { DoubleHistogramBuilder doubleHistogramBuilder = longHistogramBuilder.ofDoubles(); assertTrue(doubleHistogramBuilder instanceof CapaDoubleHistogramBuilder); } -} \ No newline at end of file +} diff --git a/sdk-component/src/test/java/group/rxcloud/capa/component/telemetry/metrics/CapaMeterProviderBuilderTest.java b/sdk-component/src/test/java/group/rxcloud/capa/component/telemetry/metrics/CapaMeterProviderBuilderTest.java index 56169e9..c80cbcb 100644 --- a/sdk-component/src/test/java/group/rxcloud/capa/component/telemetry/metrics/CapaMeterProviderBuilderTest.java +++ b/sdk-component/src/test/java/group/rxcloud/capa/component/telemetry/metrics/CapaMeterProviderBuilderTest.java @@ -19,12 +19,12 @@ import com.google.common.collect.Lists; import group.rxcloud.capa.component.telemetry.SamplerConfig; import io.opentelemetry.api.metrics.MeterProvider; -import io.opentelemetry.api.metrics.internal.NoopMeterProvider; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.concurrent.TimeUnit; +import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; /** @@ -57,7 +57,7 @@ public void buildWithEmptyConfig() { .setMeterConfig(new MeterConfig()) .buildMeterProvider(); - assertTrue(meterProvider instanceof NoopMeterProvider); + assertSame(MeterProvider.noop(), meterProvider); } @Test @@ -136,4 +136,4 @@ public void setMeterConfig() { assertTrue(Arrays.stream(lstThreads).anyMatch(t -> t.getName().contains(readerConfigByPath.getName()))); } -} \ No newline at end of file +} diff --git a/sdk-component/src/test/java/group/rxcloud/capa/component/telemetry/metrics/CapaMeterTest.java b/sdk-component/src/test/java/group/rxcloud/capa/component/telemetry/metrics/CapaMeterTest.java index e3aad1d..421de99 100644 --- a/sdk-component/src/test/java/group/rxcloud/capa/component/telemetry/metrics/CapaMeterTest.java +++ b/sdk-component/src/test/java/group/rxcloud/capa/component/telemetry/metrics/CapaMeterTest.java @@ -17,10 +17,10 @@ package group.rxcloud.capa.component.telemetry.metrics; import io.opentelemetry.api.metrics.Meter; -import io.opentelemetry.api.metrics.internal.NoopMeter; +import io.opentelemetry.api.metrics.MeterProvider; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -51,7 +51,8 @@ public void upDownCounterBuilder() { public void histogramBuilder() { Meter meter = mock(Meter.class); CapaMeter capaMeter = new CapaMeter("", "", "", meter); - assertTrue(capaMeter.histogramBuilder("aaa").setUnit("a").setDescription("desc").build() instanceof NoopMeter.NoopDoubleHistogram); + assertEquals(MeterProvider.noop().get("test").histogramBuilder("test").build().getClass(), + capaMeter.histogramBuilder("aaa").setUnit("a").setDescription("desc").build().getClass()); verify(meter, never()).histogramBuilder("aaa"); } @@ -62,4 +63,4 @@ public void gaugeBuilder() { capaMeter.gaugeBuilder("aaa"); verify(meter).gaugeBuilder("aaa"); } -} \ No newline at end of file +} diff --git a/sdk-component/src/test/java/group/rxcloud/capa/component/telemetry/metrics/TestLongHistogram.java b/sdk-component/src/test/java/group/rxcloud/capa/component/telemetry/metrics/TestLongHistogram.java index ce6b750..336fe82 100644 --- a/sdk-component/src/test/java/group/rxcloud/capa/component/telemetry/metrics/TestLongHistogram.java +++ b/sdk-component/src/test/java/group/rxcloud/capa/component/telemetry/metrics/TestLongHistogram.java @@ -17,7 +17,6 @@ package group.rxcloud.capa.component.telemetry.metrics; import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.metrics.BoundLongHistogram; import io.opentelemetry.context.Context; /** @@ -46,8 +45,4 @@ public void record(long value, Attributes attributes, Context context) { } - @Override - public BoundLongHistogram bind(Attributes attributes) { - return null; - } } diff --git a/sdk-component/src/test/java/group/rxcloud/capa/component/telemetry/trace/CapaTracerProviderBuilderTest.java b/sdk-component/src/test/java/group/rxcloud/capa/component/telemetry/trace/CapaTracerProviderBuilderTest.java index f4f2232..532ae46 100644 --- a/sdk-component/src/test/java/group/rxcloud/capa/component/telemetry/trace/CapaTracerProviderBuilderTest.java +++ b/sdk-component/src/test/java/group/rxcloud/capa/component/telemetry/trace/CapaTracerProviderBuilderTest.java @@ -37,6 +37,7 @@ import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; /** * @author: chenyijiang @@ -60,6 +61,7 @@ public void buildFromTraceConfg() { outter.setMaxAttributeValueLength(5); SpanProcessor processor = mock(SpanProcessor.class); + when(processor.isEndRequired()).thenReturn(true); CapaTracerProvider provider = new CapaTracerProviderBuilder() .setTracerConfig(config) .addProcessor(processor) @@ -134,4 +136,4 @@ public void buildFromTraceConfig() { assertTrue(TestSpanProcessor.called("span2")); } -} \ No newline at end of file +} diff --git a/sdk-component/src/test/java/group/rxcloud/capa/component/telemetry/trace/TestSpanProcessor.java b/sdk-component/src/test/java/group/rxcloud/capa/component/telemetry/trace/TestSpanProcessor.java index 2fd98bb..4ed4af4 100644 --- a/sdk-component/src/test/java/group/rxcloud/capa/component/telemetry/trace/TestSpanProcessor.java +++ b/sdk-component/src/test/java/group/rxcloud/capa/component/telemetry/trace/TestSpanProcessor.java @@ -49,7 +49,7 @@ public void onEnd(ReadableSpan span) { @Override public boolean isEndRequired() { - return false; + return true; } public static boolean called(String name) { diff --git a/sdk-component/src/test/resources/capa-component-telemetry-sampling.properties b/sdk-component/src/test/resources/capa-component-telemetry-sampling.properties new file mode 100644 index 0000000..5aeb287 --- /dev/null +++ b/sdk-component/src/test/resources/capa-component-telemetry-sampling.properties @@ -0,0 +1,2 @@ +traceEnable=true +metricsEnable=true \ No newline at end of file diff --git a/sdk-infrastructure/pom.xml b/sdk-infrastructure/pom.xml index a9b0269..26c03f7 100644 --- a/sdk-infrastructure/pom.xml +++ b/sdk-infrastructure/pom.xml @@ -23,7 +23,7 @@ capa-parent group.rxcloud - 1.10.10.RELEASE + 1.11.13.2.RELEASE capa-sdk-infrastructure @@ -59,10 +59,6 @@ io.opentelemetry opentelemetry-api - - io.opentelemetry - opentelemetry-api-metrics - @@ -138,4 +134,4 @@ - \ No newline at end of file + diff --git a/sdk-infrastructure/src/main/java/group/rxcloud/capa/infrastructure/CapaClassLoader.java b/sdk-infrastructure/src/main/java/group/rxcloud/capa/infrastructure/CapaClassLoader.java index 6429f40..3ca5700 100644 --- a/sdk-infrastructure/src/main/java/group/rxcloud/capa/infrastructure/CapaClassLoader.java +++ b/sdk-infrastructure/src/main/java/group/rxcloud/capa/infrastructure/CapaClassLoader.java @@ -81,6 +81,20 @@ public static T loadInfrastructureClassObj(String infrastructureDomain, Clas return loadClassObj(implClassPath, parameterTypes, initargs); } + /** + * Load plugin class obj. + * + * @param the target class type + * @param superClazz the interface class type + * @return the target plugin class obj + */ + public static T loadPluginClassObj(Class superClazz) { + Object pluginImpl = CapaProperties.PLUGIN_PROPERTIES_SUPPLIER.apply(superClazz); + return (T) pluginImpl; + } + + // -- Private + private static T loadClassObj(String classPath, Class[] parameterTypes, Object[] initargs) { try { Class aClass = (Class) Class.forName(classPath); diff --git a/sdk-infrastructure/src/main/java/group/rxcloud/capa/infrastructure/CapaConstants.java b/sdk-infrastructure/src/main/java/group/rxcloud/capa/infrastructure/CapaConstants.java index 752ea46..0fa36e1 100644 --- a/sdk-infrastructure/src/main/java/group/rxcloud/capa/infrastructure/CapaConstants.java +++ b/sdk-infrastructure/src/main/java/group/rxcloud/capa/infrastructure/CapaConstants.java @@ -26,14 +26,14 @@ interface CapaConstants { */ interface Properties { - /** - * The {@code infrastructure} properties prefix. - */ - String CAPA_INFRASTRUCTURE_PROPERTIES_PREFIX = "/capa-infrastructure-"; /** * The {@code component} properties prefix. */ String CAPA_COMPONENT_PROPERTIES_PREFIX = "/capa-component-"; + /** + * The {@code infrastructure} properties prefix. + */ + String CAPA_INFRASTRUCTURE_PROPERTIES_PREFIX = "/capa-infrastructure-"; /** * The properties suffix. diff --git a/sdk-infrastructure/src/main/java/group/rxcloud/capa/infrastructure/CapaProperties.java b/sdk-infrastructure/src/main/java/group/rxcloud/capa/infrastructure/CapaProperties.java index c2a4243..17ad9d8 100644 --- a/sdk-infrastructure/src/main/java/group/rxcloud/capa/infrastructure/CapaProperties.java +++ b/sdk-infrastructure/src/main/java/group/rxcloud/capa/infrastructure/CapaProperties.java @@ -27,18 +27,20 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; +import java.util.Iterator; import java.util.Map; import java.util.Objects; import java.util.Properties; +import java.util.ServiceLoader; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; -import java.util.function.Supplier; import static group.rxcloud.capa.infrastructure.CapaConstants.Properties.CAPA_COMPONENT_PROPERTIES_PREFIX; import static group.rxcloud.capa.infrastructure.CapaConstants.Properties.CAPA_INFRASTRUCTURE_PROPERTIES_PREFIX; import static group.rxcloud.capa.infrastructure.CapaConstants.Properties.CAPA_PROPERTIES_SUFFIX; import static group.rxcloud.capa.infrastructure.InnerModule.FILE_CACHE_MAP; import static group.rxcloud.capa.infrastructure.InnerModule.loadCapaConfig; +import static group.rxcloud.capa.infrastructure.InnerModule.loadCapaFileByJavaSpi; import static group.rxcloud.capa.infrastructure.InnerModule.loadCapaProperties; /** @@ -47,25 +49,16 @@ public abstract class CapaProperties { /** - * Capa's default use of HTTP. - */ - private static final Supplier DEFAULT_API_PROTOCOL = () -> "HTTP"; - - /** - * Determines if Capa client will use HTTP or Other client. - */ - public static final Supplier API_PROTOCOL = DEFAULT_API_PROTOCOL; - - /** - * Capa's default timeout in seconds for HTTP client reads. - */ - private static final Integer DEFAULT_HTTP_CLIENT_READTIMEOUTSECONDS = 60; - - /** - * Capa's timeout in seconds for HTTP client reads. + * Capa's component properties supplier. */ - public static final Supplier HTTP_CLIENT_READ_TIMEOUT_SECONDS - = () -> DEFAULT_HTTP_CLIENT_READTIMEOUTSECONDS; + public static final Function COMPONENT_PROPERTIES_SUPPLIER + = (componentDomain) -> (Properties) FILE_CACHE_MAP.computeIfAbsent(componentDomain, + s -> { + final String fileName = CAPA_COMPONENT_PROPERTIES_PREFIX + + componentDomain.toLowerCase() + + CAPA_PROPERTIES_SUFFIX; + return loadCapaProperties(fileName); + }); /** * Capa's infrastructure properties supplier. @@ -80,16 +73,11 @@ public abstract class CapaProperties { }); /** - * Capa's component properties supplier. + * Capa's plugin properties supplier. */ - public static final Function COMPONENT_PROPERTIES_SUPPLIER - = (componentDomain) -> (Properties) FILE_CACHE_MAP.computeIfAbsent(componentDomain, - s -> { - final String fileName = CAPA_COMPONENT_PROPERTIES_PREFIX - + componentDomain.toLowerCase() - + CAPA_PROPERTIES_SUFFIX; - return loadCapaProperties(fileName); - }); + public static final Function PLUGIN_PROPERTIES_SUPPLIER + = (clazz) -> FILE_CACHE_MAP.computeIfAbsent(clazz.getName(), + s -> loadCapaFileByJavaSpi(clazz)); /** * Capa's config file supplier. @@ -118,19 +106,32 @@ static Properties loadCapaProperties(final String fileName) { properties.load(inputStreamReader); return properties; } catch (IOException e) { - throw new IllegalArgumentException(fileName + " file not found."); + throw new IllegalArgumentException(fileName + " file not found.", e); } } static T loadCapaConfig(final String fileName, Class configClazz) { Objects.requireNonNull(fileName, "fileName not found."); - try (InputStream in = configClazz.getResourceAsStream(fileName)) { + try (InputStream in = CapaProperties.class.getResourceAsStream(fileName)) { InputStreamReader inputStreamReader = new InputStreamReader(in, StandardCharsets.UTF_8); return OBJECT_MAPPER.readValue(inputStreamReader, configClazz); } catch (JsonParseException | JsonMappingException e) { - throw new IllegalArgumentException(fileName + " file not load."); + throw new IllegalArgumentException(fileName + " file not load.", e); } catch (IOException e) { - throw new IllegalArgumentException(fileName + " file not found."); + throw new IllegalArgumentException(fileName + " file not found.", e); + } + } + + static T loadCapaFileByJavaSpi(Class configClazz) { + try { + ServiceLoader loader = ServiceLoader.load(configClazz); + Iterator iterator = loader.iterator(); + if (!iterator.hasNext()) { + return null; + } + return iterator.next(); + } catch (Exception e) { + throw new IllegalArgumentException(configClazz.getName() + " spi file not found.", e); } } } diff --git a/sdk-infrastructure/src/main/java/group/rxcloud/capa/infrastructure/hook/MergedPropertiesConfig.java b/sdk-infrastructure/src/main/java/group/rxcloud/capa/infrastructure/hook/MergedPropertiesConfig.java deleted file mode 100644 index fce050e..0000000 --- a/sdk-infrastructure/src/main/java/group/rxcloud/capa/infrastructure/hook/MergedPropertiesConfig.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package group.rxcloud.capa.infrastructure.hook; - -import group.rxcloud.cloudruntimes.domain.core.configuration.SubConfigurationResp; -import group.rxcloud.cloudruntimes.utils.TypeRef; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import reactor.core.publisher.Flux; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.atomic.AtomicReferenceArray; - -/** - * Config provider to merge multiple properties file which takes the input order as their priority. - *

- * - * TODO: 2021/12/3 This should not in infrastructure layer. - * TODO: 2021/12/3 Use Configuration extension api to get merged file. - */ -@Deprecated -public class MergedPropertiesConfig { - - private static final Logger log = LoggerFactory.getLogger(MergedPropertiesConfig.class); - - private final String fileName; - - private final AtomicReferenceArray properties; - - private final Object lock = new Object(); - - private volatile Map merged; - - public MergedPropertiesConfig(String fileName, String... appIds) { - this.fileName = fileName; - properties = new AtomicReferenceArray<>(appIds.length); - merged = new HashMap<>(); - Mixer.configurationHooksNullable().ifPresent(hooks -> { - for (int i = 0; i < appIds.length; i++) { - try { - subscribeConfigurationByAppId(hooks, appIds[i], i); - } catch (Throwable throwable) { - log.warn("Fail to subscribe config for app id " + appIds[i] + ", index " + i, throwable); - } - - } - }); - } - - public boolean containsKey(String key) { - return merged.containsKey(key); - } - - public String get(String key) { - return merged.get(key); - } - - public Map getMerged() { - return merged; - } - - private void subscribeConfigurationByAppId(ConfigurationHooks configurationHooks, String appId, int index) { - String storeName = configurationHooks.registryStoreNames().get(0); - - Flux> configFlux = configurationHooks.subscribeConfiguration( - storeName, - appId, - Collections.singletonList(fileName), - null, - "", - "", - TypeRef.get(Map.class)); - - // FIXME: 2021/12/3 random callback? - configFlux.subscribe(resp -> { - synchronized (lock) { - if (!resp.getItems().isEmpty()) { - properties.set(index, resp.getItems().get(0).getContent()); - } else { - properties.set(index, null); - } - - Map merged = new HashMap<>(); - for (int i = 0; i < properties.length(); i++) { - Map item = properties.get(i); - if (item != null) { - item.forEach((k, v) -> merged.putIfAbsent(String.valueOf(k), String.valueOf(v))); - } - } - this.merged = merged; - } - }); - } -} diff --git a/sdk-infrastructure/src/main/java/group/rxcloud/capa/infrastructure/plugin/PluginLoader.java b/sdk-infrastructure/src/main/java/group/rxcloud/capa/infrastructure/plugin/PluginLoader.java new file mode 100644 index 0000000..de783be --- /dev/null +++ b/sdk-infrastructure/src/main/java/group/rxcloud/capa/infrastructure/plugin/PluginLoader.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package group.rxcloud.capa.infrastructure.plugin; + +import group.rxcloud.capa.infrastructure.CapaClassLoader; + +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; + +/** + * The Plugin SPI loader. + */ +public final class PluginLoader { + + private static final Map pluginImplCache; + + static { + pluginImplCache = new ConcurrentHashMap<>(); + } + + public static Map getPluginImplCache() { + return pluginImplCache; + } + + /** + * Load plugin impl by java spi. + * + * @param the plugin interface type + * @param pluginSuperClazz the plugin interface class + * @return the optional of plugin impl + */ + public static Optional loadPluginImpl(Class pluginSuperClazz) { + Object o = pluginImplCache.computeIfAbsent(pluginSuperClazz, + aClass -> CapaClassLoader.loadPluginClassObj(pluginSuperClazz)); + return Optional.ofNullable((T) o); + } + + /** + * Load plugin impl by java spi. + * + * @param the plugin interface type + * @param pluginSuperClazz the plugin interface class + * @param defaultPluginObj the default plugin obj + * @return the plugin impl + */ + public static T loadPluginImpl(Class pluginSuperClazz, Supplier defaultPluginObj) { + Object o = pluginImplCache.computeIfAbsent(pluginSuperClazz, + aClass -> { + T pluginClassObj = CapaClassLoader.loadPluginClassObj(pluginSuperClazz); + if (pluginClassObj != null) { + return pluginClassObj; + } + return defaultPluginObj.get(); + }); + return (T) o; + } +} diff --git a/sdk-infrastructure/src/main/java/group/rxcloud/capa/infrastructure/plugin/PluginOrder.java b/sdk-infrastructure/src/main/java/group/rxcloud/capa/infrastructure/plugin/PluginOrder.java new file mode 100644 index 0000000..958dba6 --- /dev/null +++ b/sdk-infrastructure/src/main/java/group/rxcloud/capa/infrastructure/plugin/PluginOrder.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package group.rxcloud.capa.infrastructure.plugin; + +/** + * TODO: 2021/12/29 currently only support one plugin. + */ +public @interface PluginOrder { + + int order() default 0; +} diff --git a/sdk-infrastructure/src/main/java/group/rxcloud/capa/infrastructure/serializer/AbstractObjectSerializer.java b/sdk-infrastructure/src/main/java/group/rxcloud/capa/infrastructure/serializer/AbstractObjectSerializer.java new file mode 100644 index 0000000..c8076cf --- /dev/null +++ b/sdk-infrastructure/src/main/java/group/rxcloud/capa/infrastructure/serializer/AbstractObjectSerializer.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package group.rxcloud.capa.infrastructure.serializer; + +import group.rxcloud.cloudruntimes.utils.TypeRef; + +import java.io.IOException; + +public abstract class AbstractObjectSerializer implements CapaObjectSerializer { + + /** + * {@inheritDoc} + */ + @Override + public byte[] serialize(Object o) throws IOException { + if (o == null) { + return null; + } + if (o instanceof byte[]) { + return (byte[]) o; + } + if (o instanceof String) { + return ((String) o).getBytes(); + } + + return doSerialize(o); + } + + protected abstract byte[] doSerialize(Object o) throws IOException; + + /** + * {@inheritDoc} + */ + @Override + public T deserialize(byte[] data, TypeRef type) throws IOException { + Class clazz = (Class) type.getType(); + if (data == null) { + return null; + } + if (clazz == byte[].class) { + return (T) data; + } + if (clazz == String.class) { + return (T) new String(data); + } + + return doDeserialize(data, type); + } + + protected abstract T doDeserialize(byte[] data, TypeRef type) throws IOException; +} diff --git a/sdk-infrastructure/src/main/java/group/rxcloud/capa/infrastructure/serializer/DefaultObjectSerializer.java b/sdk-infrastructure/src/main/java/group/rxcloud/capa/infrastructure/serializer/DefaultObjectSerializer.java index 2cc4068..3618bb2 100644 --- a/sdk-infrastructure/src/main/java/group/rxcloud/capa/infrastructure/serializer/DefaultObjectSerializer.java +++ b/sdk-infrastructure/src/main/java/group/rxcloud/capa/infrastructure/serializer/DefaultObjectSerializer.java @@ -24,7 +24,7 @@ /** * Default serializer/deserializer for request/response objects. */ -public class DefaultObjectSerializer extends ObjectSerializer implements CapaObjectSerializer { +public class DefaultObjectSerializer extends ExtensionObjectSerializer implements CapaObjectSerializer { /** * {@inheritDoc} diff --git a/sdk-infrastructure/src/main/java/group/rxcloud/capa/infrastructure/serializer/ObjectSerializer.java b/sdk-infrastructure/src/main/java/group/rxcloud/capa/infrastructure/serializer/ExtensionObjectSerializer.java similarity index 85% rename from sdk-infrastructure/src/main/java/group/rxcloud/capa/infrastructure/serializer/ObjectSerializer.java rename to sdk-infrastructure/src/main/java/group/rxcloud/capa/infrastructure/serializer/ExtensionObjectSerializer.java index 3f84b49..211f4a6 100644 --- a/sdk-infrastructure/src/main/java/group/rxcloud/capa/infrastructure/serializer/ObjectSerializer.java +++ b/sdk-infrastructure/src/main/java/group/rxcloud/capa/infrastructure/serializer/ExtensionObjectSerializer.java @@ -31,7 +31,7 @@ /** * Serializes and deserializes an internal object. */ -public class ObjectSerializer { +public abstract class ExtensionObjectSerializer extends AbstractObjectSerializer { /** * Shared Json serializer/deserializer as per Jackson's documentation. @@ -44,7 +44,7 @@ public class ObjectSerializer { /** * Default constructor to avoid class from being instantiated outside package but still inherited. */ - protected ObjectSerializer() { + protected ExtensionObjectSerializer() { } /** @@ -55,33 +55,31 @@ protected ObjectMapper getObjectMapper() { } /** - * Serializes a given state object into byte array. + * Serializes a given object into byte array. * - * @param state State object to be serialized. + * @param o object to be serialized. * @return Array of bytes[] with the serialized content. - * @throws IOException In case state cannot be serialized. + * @throws IOException In case o cannot be serialized. */ - public byte[] serialize(Object state) throws IOException { - if (state == null) { + @Override + public byte[] doSerialize(Object o) throws IOException { + if (o == null) { return null; } - - if (state.getClass() == Void.class) { + if (o.getClass() == Void.class) { return null; } - // Have this check here to be consistent with deserialization (see deserialize() method below). - if (state instanceof byte[]) { - return (byte[]) state; + if (o instanceof byte[]) { + return (byte[]) o; } - // Proto buffer class is serialized directly. - if (state instanceof MessageLite) { - return ((MessageLite) state).toByteArray(); + if (o instanceof MessageLite) { + return ((MessageLite) o).toByteArray(); } // Not string, not primitive, so it is a complex type: we use JSON for that. - return getObjectMapper().writeValueAsBytes(state); + return getObjectMapper().writeValueAsBytes(o); } /** @@ -93,8 +91,9 @@ public byte[] serialize(Object state) throws IOException { * @return Object of type T. * @throws IOException In case content cannot be deserialized. */ - public T deserialize(byte[] content, TypeRef type) throws IOException { - return deserialize(content, getObjectMapper().constructType(type.getType())); + @Override + public T doDeserialize(byte[] content, TypeRef type) throws IOException { + return doDeserialize(content, getObjectMapper().constructType(type.getType())); } /** @@ -106,11 +105,11 @@ public T deserialize(byte[] content, TypeRef type) throws IOException { * @return Object of type T. * @throws IOException In case content cannot be deserialized. */ - public T deserialize(byte[] content, Class clazz) throws IOException { - return deserialize(content, getObjectMapper().constructType(clazz)); + public T doDeserialize(byte[] content, Class clazz) throws IOException { + return doDeserialize(content, getObjectMapper().constructType(clazz)); } - private T deserialize(byte[] content, JavaType javaType) throws IOException { + private T doDeserialize(byte[] content, JavaType javaType) throws IOException { if ((javaType == null) || javaType.isTypeOrSubTypeOf(Void.class)) { return null; } diff --git a/sdk-infrastructure/src/main/java/group/rxcloud/capa/infrastructure/utils/SpiUtils.java b/sdk-infrastructure/src/main/java/group/rxcloud/capa/infrastructure/utils/SpiUtils.java index d48ea21..0bf4b12 100644 --- a/sdk-infrastructure/src/main/java/group/rxcloud/capa/infrastructure/utils/SpiUtils.java +++ b/sdk-infrastructure/src/main/java/group/rxcloud/capa/infrastructure/utils/SpiUtils.java @@ -21,8 +21,6 @@ import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import group.rxcloud.capa.infrastructure.CapaProperties; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -40,6 +38,7 @@ * Load class and create instance from config file. * TODO move to {@link CapaProperties} */ +@Deprecated public final class SpiUtils { private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper() @@ -48,8 +47,6 @@ public final class SpiUtils { private static final Map CACHE = new ConcurrentHashMap<>(); - private static final Logger log = LoggerFactory.getLogger(SpiUtils.class); - private SpiUtils() { } @@ -58,46 +55,12 @@ public static T loadConfigNullable(String path, Class configType) { if (in != null) { InputStreamReader inputStreamReader = new InputStreamReader(in, StandardCharsets.UTF_8); return OBJECT_MAPPER.readValue(inputStreamReader, configType); - } else { - log.warn(path + " file not found."); - } - } catch (IOException e) { - log.warn(path + " config file not found.", e); - } - return null; - } - - public static Properties loadPropertiesNullable(String path) { - try (InputStream in = SpiUtils.class.getResourceAsStream(path)) { - if (in != null) { - InputStreamReader inputStreamReader = new InputStreamReader(in, StandardCharsets.UTF_8); - Properties properties = new Properties(); - properties.load(inputStreamReader); - return properties; - } else { - log.warn(path + " file not found."); } } catch (IOException e) { - log.warn(path + " file not found.", e); } return null; } - public static Properties loadProperties(String path) { - try (InputStream in = SpiUtils.class.getResourceAsStream(path)) { - if (in != null) { - InputStreamReader inputStreamReader = new InputStreamReader(in, StandardCharsets.UTF_8); - Properties properties = new Properties(); - properties.load(inputStreamReader); - return properties; - } else { - throw new IllegalArgumentException(path + " file not found."); - } - } catch (IOException e) { - throw new IllegalArgumentException(path + " file not found.", e); - } - } - @Nullable public static T loadFromSpiComponentFileNullable(Class type, String fileSuffix) { return loadFromSpiComponentFileNullable(type, null, null, fileSuffix, false); @@ -114,7 +77,6 @@ public static T loadFromSpiComponentFileNullable(Class type, Class[] argT } return null; } catch (Throwable e) { - log.info("Fail to load " + type.getName() + " instance from spi config file."); } return null; } diff --git a/sdk-infrastructure/src/test/java/group/rxcloud/capa/infrastructure/CapaPropertiesTest.java b/sdk-infrastructure/src/test/java/group/rxcloud/capa/infrastructure/CapaPropertiesTest.java index 1410b65..11c803c 100644 --- a/sdk-infrastructure/src/test/java/group/rxcloud/capa/infrastructure/CapaPropertiesTest.java +++ b/sdk-infrastructure/src/test/java/group/rxcloud/capa/infrastructure/CapaPropertiesTest.java @@ -23,18 +23,6 @@ public class CapaPropertiesTest { - @Test - public void testGetApiProtocol_Success() { - String apiProtocol = CapaProperties.API_PROTOCOL.get(); - Assertions.assertEquals("HTTP", apiProtocol); - } - - @Test - public void testGetHttpClientReadTimeoutSeconds_Success() { - Integer httpClientReadTimeoutSeconds = CapaProperties.HTTP_CLIENT_READ_TIMEOUT_SECONDS.get(); - Assertions.assertEquals(60, httpClientReadTimeoutSeconds.intValue()); - } - @Test public void testGetComponentProperties_Success() { Properties properties = CapaProperties.COMPONENT_PROPERTIES_SUPPLIER.apply("rpc"); diff --git a/sdk-infrastructure/src/test/java/group/rxcloud/capa/infrastructure/plugin/Plugin.java b/sdk-infrastructure/src/test/java/group/rxcloud/capa/infrastructure/plugin/Plugin.java new file mode 100644 index 0000000..c2c38bf --- /dev/null +++ b/sdk-infrastructure/src/test/java/group/rxcloud/capa/infrastructure/plugin/Plugin.java @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package group.rxcloud.capa.infrastructure.plugin; + +public interface Plugin { + + String sayHello(); +} diff --git a/sdk-infrastructure/src/test/java/group/rxcloud/capa/infrastructure/plugin/PluginImpl.java b/sdk-infrastructure/src/test/java/group/rxcloud/capa/infrastructure/plugin/PluginImpl.java new file mode 100644 index 0000000..87c2f7d --- /dev/null +++ b/sdk-infrastructure/src/test/java/group/rxcloud/capa/infrastructure/plugin/PluginImpl.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package group.rxcloud.capa.infrastructure.plugin; + +public class PluginImpl implements Plugin { + + @Override + public String sayHello() { + return "hello"; + } +} diff --git a/sdk-infrastructure/src/test/java/group/rxcloud/capa/infrastructure/plugin/PluginLoaderTest.java b/sdk-infrastructure/src/test/java/group/rxcloud/capa/infrastructure/plugin/PluginLoaderTest.java new file mode 100644 index 0000000..9855799 --- /dev/null +++ b/sdk-infrastructure/src/test/java/group/rxcloud/capa/infrastructure/plugin/PluginLoaderTest.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package group.rxcloud.capa.infrastructure.plugin; + +import org.junit.jupiter.api.Test; + +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class PluginLoaderTest { + + @Test + public void loadPluginImpl_spi() { + Optional pluginOp = PluginLoader.loadPluginImpl(Plugin.class); + Plugin plugin = pluginOp.get(); + assertEquals(plugin.sayHello(), "hello"); + } + + @Test + public void loadPluginImpl_default() { + Plugin plugin = PluginLoader.loadPluginImpl(Plugin.class, () -> new Plugin() { + @Override + public String sayHello() { + return "hhh"; + } + }); + assertEquals(plugin.sayHello(), "hello"); + } +} \ No newline at end of file diff --git a/sdk-infrastructure/src/test/java/group/rxcloud/capa/infrastructure/utils/SpiUtilsTest.java b/sdk-infrastructure/src/test/java/group/rxcloud/capa/infrastructure/utils/SpiUtilsTest.java index ddefc30..9e0ab08 100644 --- a/sdk-infrastructure/src/test/java/group/rxcloud/capa/infrastructure/utils/SpiUtilsTest.java +++ b/sdk-infrastructure/src/test/java/group/rxcloud/capa/infrastructure/utils/SpiUtilsTest.java @@ -19,7 +19,6 @@ import org.junit.jupiter.api.Test; import java.util.List; -import java.util.Properties; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -39,30 +38,6 @@ public void loadConfigNullable() { assertNull(SpiUtils.loadConfigNullable("/config.json", Integer.class)); } - @Test - public void loadPropertiesNullable() { - Properties properties = SpiUtils.loadPropertiesNullable("/config.properties"); - assertEquals("aaa", properties.getProperty("str")); - - assertNull(SpiUtils.loadPropertiesNullable("lalala")); - } - - @Test - public void loadProperties() { - Properties properties = SpiUtils.loadProperties("/config.properties"); - assertEquals("aaa", properties.getProperty("str")); - } - - @Test - public void loadPropertiesFail() { - Throwable t = null; - try { - Properties properties = SpiUtils.loadProperties("aaaa"); - } catch (Throwable throwable) { - t = throwable; - } - assertNotNull(t); - } @Test public void loadFromSpiComponentFileNullable() { MyInterface myInterface = SpiUtils.loadFromSpiComponentFileNullable(MyInterface.class, "test"); diff --git a/sdk-infrastructure/src/test/resources/META-INF/services/group.rxcloud.capa.infrastructure.plugin.Plugin b/sdk-infrastructure/src/test/resources/META-INF/services/group.rxcloud.capa.infrastructure.plugin.Plugin new file mode 100644 index 0000000..7ce97bc --- /dev/null +++ b/sdk-infrastructure/src/test/resources/META-INF/services/group.rxcloud.capa.infrastructure.plugin.Plugin @@ -0,0 +1 @@ +group.rxcloud.capa.infrastructure.plugin.PluginImpl \ No newline at end of file diff --git a/sdk-spi-demo/pom.xml b/sdk-spi-demo/pom.xml index 8a0d81c..c817fe5 100644 --- a/sdk-spi-demo/pom.xml +++ b/sdk-spi-demo/pom.xml @@ -23,7 +23,7 @@ capa-parent group.rxcloud - 1.10.10.RELEASE + 1.11.13.2.RELEASE capa-sdk-spi-demo @@ -31,8 +31,9 @@ capa-sdk-spi-demo - 2.14.1 - 1.2.3 + 2.25.4 + 1.3.16 + 2.0.17 @@ -123,4 +124,4 @@ - \ No newline at end of file + diff --git a/sdk-spi/pom.xml b/sdk-spi/pom.xml index dae108e..6fd761e 100644 --- a/sdk-spi/pom.xml +++ b/sdk-spi/pom.xml @@ -23,7 +23,7 @@ capa-parent group.rxcloud - 1.10.10.RELEASE + 1.11.13.2.RELEASE capa-sdk-spi @@ -31,8 +31,9 @@ capa-sdk-spi - 2.14.1 - 1.2.3 + 2.25.4 + 1.3.16 + 2.0.17 @@ -114,4 +115,4 @@ - \ No newline at end of file + diff --git a/sdk-spi/src/main/java/group/rxcloud/capa/spi/configstore/CapaConfigStoreSpi.java b/sdk-spi/src/main/java/group/rxcloud/capa/spi/configstore/CapaConfigStoreSpi.java index 09901ae..14c2506 100644 --- a/sdk-spi/src/main/java/group/rxcloud/capa/spi/configstore/CapaConfigStoreSpi.java +++ b/sdk-spi/src/main/java/group/rxcloud/capa/spi/configstore/CapaConfigStoreSpi.java @@ -50,7 +50,7 @@ public CapaConfigStoreSpi(CapaObjectSerializer objectSerializer) { @Override public Mono>> get(GetRequest getRequest, TypeRef type) { if (logger.isDebugEnabled()) { - logger.debug("[CapaConfigStoreSpi] get config request[{}]", getRequest); + logger.debug("[Capa.Config] [CapaConfigStoreSpi] get config request[{}]", getRequest); } final String appId = getRequest.getAppId(); final String group = getRequest.getGroup(); @@ -84,7 +84,7 @@ protected abstract Mono>> doGet(String appId, @Override public Flux> subscribe(SubscribeReq subscribeReq, TypeRef type) { if (logger.isDebugEnabled()) { - logger.debug("[CapaConfigStoreSpi] subscribe config request[{}]", subscribeReq); + logger.debug("[Capa.Config] [CapaConfigStoreSpi] subscribe config request[{}]", subscribeReq); } final String appId = subscribeReq.getAppId(); final String group = subscribeReq.getGroup(); diff --git a/sdk-spi/src/main/java/group/rxcloud/capa/spi/http/CapaHttpSpi.java b/sdk-spi/src/main/java/group/rxcloud/capa/spi/http/CapaHttpSpi.java index 6d37d7d..e2b3291 100644 --- a/sdk-spi/src/main/java/group/rxcloud/capa/spi/http/CapaHttpSpi.java +++ b/sdk-spi/src/main/java/group/rxcloud/capa/spi/http/CapaHttpSpi.java @@ -18,6 +18,8 @@ import group.rxcloud.capa.component.http.CapaHttp; import group.rxcloud.capa.component.http.HttpResponse; +import group.rxcloud.capa.infrastructure.exceptions.CapaErrorContext; +import group.rxcloud.capa.infrastructure.exceptions.CapaException; import group.rxcloud.capa.infrastructure.serializer.CapaObjectSerializer; import group.rxcloud.capa.spi.http.config.CapaSpiOptionsLoader; import group.rxcloud.capa.spi.http.config.CapaSpiProperties; @@ -63,16 +65,16 @@ protected CompletableFuture> doInvokeApi(String httpMethod, TypeRef type) { if (logger.isDebugEnabled()) { if (httpMethod != null) { - logger.debug("[CapaHttpSpi] invoke rpc httpMethod[{}]", httpMethod); + logger.debug("[Capa.Rpc.Client.http] [CapaHttpSpi] invoke rpc httpMethod[{}]", httpMethod); } if (urlParameters != null && !urlParameters.isEmpty()) { - logger.debug("[CapaHttpSpi] invoke rpc urlParameters[{}]", urlParameters); + logger.debug("[Capa.Rpc.Client.http] [CapaHttpSpi] invoke rpc urlParameters[{}]", urlParameters); } if (headers != null && !headers.isEmpty()) { - logger.debug("[CapaHttpSpi] invoke rpc headers[{}}]", headers); + logger.debug("[Capa.Rpc.Client.http] [CapaHttpSpi] invoke rpc headers[{}}]", headers); } if (context != null) { - logger.debug("[CapaHttpSpi] invoke rpc context[{}]", context); + logger.debug("[Capa.Rpc.Client.http] [CapaHttpSpi] invoke rpc context[{}]", context); } } @@ -84,7 +86,7 @@ protected CompletableFuture> doInvokeApi(String httpMethod, final String _method = pathSegments[3]; final String method = pathSegments[4]; if (logger.isDebugEnabled()) { - logger.debug("[CapaHttpSpi] invoke rpc url_path[/version={}/{}/appId={}/{}/methodName={}]", + logger.debug("[Capa.Rpc.Client.http] [CapaHttpSpi] invoke rpc url_path[/version={}/{}/appId={}/{}/methodName={}]", version, _invoke, appId, _method, method); } @@ -92,37 +94,24 @@ protected CompletableFuture> doInvokeApi(String httpMethod, RpcServiceOptions rpcServiceOptions = getRpcServiceOptions(appId); Objects.requireNonNull(rpcServiceOptions, "rpcServiceOptions"); if (logger.isDebugEnabled()) { - logger.debug("[CapaHttpSpi] invoke rpc options[{}]", + logger.debug("[Capa.Rpc.Client.http] [CapaHttpSpi] invoke rpc options[{}]", rpcServiceOptions); } - // spi invoke - CompletableFuture> invokeSpiApi = - invokeSpiApi(appId, method, requestData, httpMethod, headers, urlParameters, type, rpcServiceOptions); - invokeSpiApi.whenComplete((tHttpResponse, throwable) -> { - if (throwable != null) { - if (logger.isWarnEnabled()) { - logger.warn("[CapaHttpSpi] invoke rpc response error", - throwable); - } - return; - } - if (tHttpResponse == null) { - if (logger.isWarnEnabled()) { - logger.warn("[CapaHttpSpi] invoke rpc response empty[{}]", - tHttpResponse); - } - return; + try { + // spi invoke + CompletableFuture> invokeSpiApi = invokeSpiApi( + appId, method, requestData, httpMethod, headers, urlParameters, type, rpcServiceOptions); + invokeSpiApi.whenComplete(this::callbackLog); + return invokeSpiApi; + } catch (CapaException e) { + throw e; + } catch (Exception e) { + if (logger.isErrorEnabled()) { + logger.error("[Capa.Rpc.Client.http] [CapaHttpSpi] invoke error, un-catch throwable is: ", e); } - final int responseStatusCode = tHttpResponse.getStatusCode(); - final Map responseHeaders = tHttpResponse.getHeaders(); - final T responseBody = tHttpResponse.getBody(); - if (logger.isDebugEnabled()) { - logger.debug("[CapaHttpSpi] invoke rpc response code[{}] headers[{}] body[{}]", - responseStatusCode, responseHeaders, responseBody); - } - }); - return invokeSpiApi; + throw new CapaException(CapaErrorContext.SYSTEM_ERROR, e); + } } /** @@ -136,6 +125,35 @@ protected RpcServiceOptions getRpcServiceOptions(String appId) { return capaSpiOptionsLoader.loadRpcServiceOptions(appId); } + private void callbackLog(HttpResponse tHttpResponse, Throwable throwable) { + if (throwable != null) { + if (throwable instanceof CapaException) { + return; + } + // un-catch throwable + else { + if (logger.isErrorEnabled()) { + logger.error("[Capa.Rpc.Client.http.callback] [CapaHttpSpi] invoke rpc response error", + throwable); + } + return; + } + } + if (tHttpResponse == null) { + if (logger.isWarnEnabled()) { + logger.warn("[Capa.Rpc.Client.http.callback] [CapaHttpSpi] invoke rpc response empty."); + } + return; + } + final int responseStatusCode = tHttpResponse.getStatusCode(); + final Map responseHeaders = tHttpResponse.getHeaders(); + final T responseBody = tHttpResponse.getBody(); + if (logger.isDebugEnabled()) { + logger.debug("[Capa.Rpc.Client.http.callback] [CapaHttpSpi] invoke rpc response code[{}] headers[{}] body[{}]", + responseStatusCode, responseHeaders, responseBody); + } + } + /** * Invoke spi api and then return async completable future. * diff --git a/sdk-spi/src/main/java/group/rxcloud/capa/spi/http/CapaSerializeHttpSpi.java b/sdk-spi/src/main/java/group/rxcloud/capa/spi/http/CapaSerializeHttpSpi.java index f434590..1e0324e 100644 --- a/sdk-spi/src/main/java/group/rxcloud/capa/spi/http/CapaSerializeHttpSpi.java +++ b/sdk-spi/src/main/java/group/rxcloud/capa/spi/http/CapaSerializeHttpSpi.java @@ -16,7 +16,6 @@ */ package group.rxcloud.capa.spi.http; -import com.fasterxml.jackson.databind.ObjectMapper; import group.rxcloud.capa.component.http.HttpResponse; import group.rxcloud.capa.infrastructure.exceptions.CapaErrorContext; import group.rxcloud.capa.infrastructure.exceptions.CapaException; @@ -38,9 +37,15 @@ import java.io.IOException; import java.io.Serializable; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import static group.rxcloud.cloudruntimes.domain.core.invocation.Metadata.ACCEPT; /** * The Capa http spi with default serializer process. @@ -60,29 +65,52 @@ public CapaSerializeHttpSpi(OkHttpClient httpClient, CapaObjectSerializer object } /** - * Gets request serialization format. - * - * @param requestData the request data - * @return the request with byte[] serialize + * HTTP invoke facade. */ - protected byte[] getRequestWithSerialize(Object requestData) { - try { - return objectSerializer.serialize(requestData); - } catch (IOException e) { - if (logger.isWarnEnabled()) { - logger.warn("[CapaSerializeHttpSpi] serialize rpc request[{}] io error", - requestData, e); + protected CompletableFuture> invokeHttpFacade(String url, + Object requestData, + String httpMethod, + Map headers, + Map> urlParameters, + TypeRef type) { + // get http header: content-type + final String contentType = getRequestContentType(headers); + + // generate http request body + RequestBody body = getRequestBodyWithSerialize(requestData, contentType); + + // get http header: accept + getRequestAcceptType(headers, body); + + // generate http headers + Headers header = getRequestHeaderWithParams(headers); + + if (logger.isDebugEnabled()) { + logger.debug("[Capa.Rpc.Client.http] [CapaSerializeHttpSpi] final request url[{}] header[{}] httpMethod[{}]", + url, header, httpMethod); + } + + // make http request + Request request = new Request.Builder() + .url(url) + .headers(header) + .method(httpMethod, body) + .build(); + + CompletableFuture> asyncInvoke0 = doAsyncInvoke0(request, type); + asyncInvoke0.exceptionally(throwable -> { + if (throwable instanceof CapaException) { + throw (CapaException) throwable; } - throw new CapaException(CapaErrorContext.PARAMETER_RPC_REQUEST_SERIALIZE_ERROR, - "Request Type: " + requestData.getClass().getName() + ", Error: " + e.getMessage()); - } catch (Exception e) { - if (logger.isWarnEnabled()) { - logger.warn("[CapaSerializeHttpSpi] serialize rpc request[{}] error", - requestData, e); + // un-catch throwable + else { + if (logger.isErrorEnabled()) { + logger.error("[Capa.Rpc.Client.http.callback] [CapaSerializeHttpSpi] async invoke error, un-catch throwable is: ", throwable); + } + throw new CapaException(CapaErrorContext.SYSTEM_ERROR, throwable); } - throw new CapaException(CapaErrorContext.PARAMETER_RPC_REQUEST_SERIALIZE_ERROR, - "Request Type: " + requestData.getClass().getName() + ", Error: " + e.getMessage(), e); - } + }); + return asyncInvoke0; } /** @@ -105,12 +133,9 @@ protected String getRequestContentType(Map headers) { * Gets request body with byte[] serialize. * * @param requestData the request data - * @param headers the headers * @return the request body with byte[] serialize */ - protected RequestBody getRequestBodyWithSerialize(Object requestData, Map headers) { - final String contentType = getRequestContentType(headers); - + protected RequestBody getRequestBodyWithSerialize(Object requestData, String contentType) { final MediaType mediaType = contentType == null ? MEDIA_TYPE_APPLICATION_JSON : MediaType.get(contentType); @@ -124,9 +149,65 @@ protected RequestBody getRequestBodyWithSerialize(Object requestData, Map headers, RequestBody body) { + final List accepts = new ArrayList<>(3); + // 1. set user accept header + final String userAcceptValue = headers.get(ACCEPT); + if (userAcceptValue != null && userAcceptValue.length() > 0) { + accepts.add(userAcceptValue); + } + // 2. set accept header same with content-type + if (body.contentType() != null) { + final String contentType = Objects.requireNonNull(body.contentType()).toString(); + if (contentType.length() > 0) { + accepts.add(contentType); + } + } + // 3. add */* at last + accepts.add("*/*"); + + final String acceptStr = accepts.stream() + .distinct() + .collect(Collectors.joining(",")); + headers.put(ACCEPT, acceptStr); + } + /** * Gets request headers with given params. * @@ -183,15 +264,15 @@ protected HttpResponse getResponseBodyWithDeserialize(TypeRef type, Ht return new HttpResponse<>(responseObject, httpResponseHeaders, httpResponseStatusCode); } catch (IOException e) { if (logger.isWarnEnabled()) { - logger.warn("[CapaSerializeHttpSpi] deserialize rpc statusCode[{}] headers[{}] response[{}] type[{}] io error", - httpResponseStatusCode, httpResponseHeaders, httpResponseBody, type, e); + logger.warn("[Capa.Rpc.Client.http.callback] [CapaSerializeHttpSpi] deserialize response statusCode[{}] headers[{}] response[{}] type[{}] io error", + httpResponseStatusCode, httpResponseHeaders, httpResponseBody, type.getType().getTypeName(), e); } throw new CapaException(CapaErrorContext.PARAMETER_RPC_RESPONSE_DESERIALIZE_ERROR, - "Response Type: " + type.getType().getTypeName() + ", Error: " + e.getMessage(), e); + "Response Type: " + type.getType().getTypeName() + ", IO Error: " + e.getMessage(), e); } catch (Exception e) { if (logger.isWarnEnabled()) { - logger.warn("[CapaSerializeHttpSpi] deserialize rpc statusCode[{}] headers[{}] response[{}] type[{}] error", - httpResponseStatusCode, httpResponseHeaders, httpResponseBody, type, e); + logger.warn("[Capa.Rpc.Client.http.callback] [CapaSerializeHttpSpi] deserialize response statusCode[{}] headers[{}] response[{}] type[{}] error", + httpResponseStatusCode, httpResponseHeaders, httpResponseBody, type.getType().getTypeName(), e); } throw new CapaException(CapaErrorContext.PARAMETER_RPC_RESPONSE_DESERIALIZE_ERROR, "Response Type: " + type.getType().getTypeName() + ", Error: " + e.getMessage(), e); @@ -208,11 +289,6 @@ protected static class SerializationResponseFutureCallback implements Callback, */ private static final byte[] EMPTY_BYTES = new byte[0]; - /** - * JSON Object Mapper. - */ - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - private final transient CompletableFuture> future; /** @@ -250,23 +326,6 @@ public void onResponse(Call call, Response response) throws IOException { future.complete(httpResponse); } - private void onResponseError(Response response, byte[] bodyBytes) { - try { - CapaException error = parseCapaError(bodyBytes); - if (error != null) { - if (error.getErrorCodeContext() != null) { - future.completeExceptionally(new CapaException(error.getErrorCodeContext(), - "HTTP status code: " + response.code())); - return; - } - } - future.completeExceptionally(new CapaException(CapaErrorContext.DEPENDENT_SERVICE_ERROR, - "HTTP status code: " + response.code())); - } catch (CapaException e) { - future.completeExceptionally(e); - } - } - private static byte[] getBodyBytesOrEmptyArray(Response response) throws IOException { ResponseBody body = response.body(); if (body != null) { @@ -275,24 +334,39 @@ private static byte[] getBodyBytesOrEmptyArray(Response response) throws IOExcep return EMPTY_BYTES; } + private void onResponseError(Response response, byte[] bodyBytes) { + try { + String error = parseCapaError(bodyBytes); + if (logger.isWarnEnabled()) { + logger.warn("[Capa.Rpc.Client.http.callback] [FutureCallback.onResponseError] response statusCode[{}], errorMsg[{}]", + response.code(), error); + } + CapaException capaException = new CapaException(CapaErrorContext.DEPENDENT_SERVICE_ERROR, + "HTTP status code: " + response.code() + ", error message: " + error); + future.completeExceptionally(capaException); + } catch (CapaException e) { + future.completeExceptionally(e); + } + } + /** * Tries to parse an error from Capa response body. * - * @param json Response body from Capa remote. - * @return CapaError or null if could not parse. + * @param bytes Response body from Capa remote. + * @return ErrorMsg or null if could not parse. */ - private static CapaException parseCapaError(byte[] json) { - if ((json == null) || (json.length == 0)) { + private static String parseCapaError(byte[] bytes) { + if ((bytes == null) || (bytes.length == 0)) { return null; } try { - return OBJECT_MAPPER.readValue(json, CapaException.class); - } catch (IOException e) { - String errorMessage = new String(json, StandardCharsets.UTF_8); - throw new CapaException(CapaErrorContext.DEPENDENT_SERVICE_ERROR, errorMessage, e); + return new String(bytes, StandardCharsets.UTF_8); } catch (Exception e) { - String errorMessage = new String(json, StandardCharsets.UTF_8); - throw new CapaException(CapaErrorContext.SYSTEM_ERROR, errorMessage, e); + if (logger.isErrorEnabled()) { + logger.error("[Capa.Rpc.Client.http.callback] [FutureCallback.parseCapaError] bytes[{}] parse error", + bytes, e); + } + throw new CapaException(CapaErrorContext.SYSTEM_ERROR, e); } } } diff --git a/sdk-spi/src/main/java/group/rxcloud/capa/spi/http/config/CapaSpiProperties.java b/sdk-spi/src/main/java/group/rxcloud/capa/spi/http/config/CapaSpiProperties.java index 460d766..5a1550f 100644 --- a/sdk-spi/src/main/java/group/rxcloud/capa/spi/http/config/CapaSpiProperties.java +++ b/sdk-spi/src/main/java/group/rxcloud/capa/spi/http/config/CapaSpiProperties.java @@ -23,13 +23,27 @@ */ public abstract class CapaSpiProperties { + /** + * Static lock object. + */ + private static final Object LOCK = new Object(); + + private static volatile CapaSpiOptionsLoader instance; + /** * Gets default options loader. * * @return the default options loader */ public static CapaSpiOptionsLoader getSpiOptionsLoader() { + if (instance == null) { + synchronized (LOCK) { + if (instance == null) { + instance = CapaClassLoader.loadComponentClassObj("rpc-common", CapaSpiOptionsLoader.class); + } + } + } // load spi rpc impl - return CapaClassLoader.loadComponentClassObj("rpc-common", CapaSpiOptionsLoader.class); + return instance; } } diff --git a/sdk-spi/src/main/java/group/rxcloud/capa/spi/telemetry/CapaContextAsyncWrapperSpi.java b/sdk-spi/src/main/java/group/rxcloud/capa/spi/telemetry/CapaContextAsyncWrapperSpi.java index 8062b67..55cf4c4 100644 --- a/sdk-spi/src/main/java/group/rxcloud/capa/spi/telemetry/CapaContextAsyncWrapperSpi.java +++ b/sdk-spi/src/main/java/group/rxcloud/capa/spi/telemetry/CapaContextAsyncWrapperSpi.java @@ -22,5 +22,4 @@ * SPI Capa context async wrapper. */ public abstract class CapaContextAsyncWrapperSpi implements CapaContextAsyncWrapper { - } diff --git a/sdk-spi/src/main/java/group/rxcloud/capa/spi/telemetry/CapaDoubleHistogramSpi.java b/sdk-spi/src/main/java/group/rxcloud/capa/spi/telemetry/CapaDoubleHistogramSpi.java index f0754d3..80299b5 100644 --- a/sdk-spi/src/main/java/group/rxcloud/capa/spi/telemetry/CapaDoubleHistogramSpi.java +++ b/sdk-spi/src/main/java/group/rxcloud/capa/spi/telemetry/CapaDoubleHistogramSpi.java @@ -19,9 +19,20 @@ import group.rxcloud.capa.component.telemetry.metrics.CapaDoubleHistogram; /** + * The Capa double histogram spi. */ public abstract class CapaDoubleHistogramSpi extends CapaDoubleHistogram { + /** + * Instantiates a new Capa double histogram spi. + * + * @param meterName the meter name + * @param schemaUrl the schema url + * @param version the version + * @param name the name + * @param description the description + * @param unit the unit + */ public CapaDoubleHistogramSpi(String meterName, String schemaUrl, String version, String name, String description, String unit) { super(meterName, schemaUrl, version, name, description, unit); } diff --git a/sdk-spi/src/main/java/group/rxcloud/capa/spi/telemetry/CapaLongHistogramSpi.java b/sdk-spi/src/main/java/group/rxcloud/capa/spi/telemetry/CapaLongHistogramSpi.java index a07f545..0beb1f8 100644 --- a/sdk-spi/src/main/java/group/rxcloud/capa/spi/telemetry/CapaLongHistogramSpi.java +++ b/sdk-spi/src/main/java/group/rxcloud/capa/spi/telemetry/CapaLongHistogramSpi.java @@ -19,9 +19,20 @@ import group.rxcloud.capa.component.telemetry.metrics.CapaLongHistogram; /** + * The Capa long histogram spi. */ public abstract class CapaLongHistogramSpi extends CapaLongHistogram { + /** + * Instantiates a new Capa long histogram spi. + * + * @param meterName the meter name + * @param schemaUrl the schema url + * @param version the version + * @param name the name + * @param description the description + * @param unit the unit + */ public CapaLongHistogramSpi(String meterName, String schemaUrl, String version, String name, String description, String unit) { super(meterName, schemaUrl, version, name, description, unit); diff --git a/sdk-spi/src/main/java/group/rxcloud/capa/spi/telemetry/CapaMeterSpi.java b/sdk-spi/src/main/java/group/rxcloud/capa/spi/telemetry/CapaMeterSpi.java index 7f31300..7731f92 100644 --- a/sdk-spi/src/main/java/group/rxcloud/capa/spi/telemetry/CapaMeterSpi.java +++ b/sdk-spi/src/main/java/group/rxcloud/capa/spi/telemetry/CapaMeterSpi.java @@ -24,6 +24,14 @@ */ public abstract class CapaMeterSpi extends CapaMeter { + /** + * Instantiates a new Capa meter spi. + * + * @param meterName the meter name + * @param schemaUrl the schema url + * @param version the version + * @param meter the meter + */ public CapaMeterSpi(String meterName, String schemaUrl, String version, Meter meter) { super(meterName, schemaUrl, version, meter); } diff --git a/sdk-spi/src/main/java/group/rxcloud/capa/spi/telemetry/CapaMetricsExporterSpi.java b/sdk-spi/src/main/java/group/rxcloud/capa/spi/telemetry/CapaMetricsExporterSpi.java index a0fb3a7..f429078 100644 --- a/sdk-spi/src/main/java/group/rxcloud/capa/spi/telemetry/CapaMetricsExporterSpi.java +++ b/sdk-spi/src/main/java/group/rxcloud/capa/spi/telemetry/CapaMetricsExporterSpi.java @@ -22,10 +22,15 @@ import java.util.function.Supplier; /** - * + * The Capa metrics exporter spi. */ public abstract class CapaMetricsExporterSpi extends CapaMetricsExporter { + /** + * Instantiates a new Capa metrics exporter spi. + * + * @param samplerConfig the sampler config + */ public CapaMetricsExporterSpi(Supplier samplerConfig) { super(samplerConfig); } diff --git a/sdk-spi/src/main/java/group/rxcloud/capa/spi/telemetry/CapaReadWriteSpanSpi.java b/sdk-spi/src/main/java/group/rxcloud/capa/spi/telemetry/CapaReadWriteSpanSpi.java index a317306..08cd88a 100644 --- a/sdk-spi/src/main/java/group/rxcloud/capa/spi/telemetry/CapaReadWriteSpanSpi.java +++ b/sdk-spi/src/main/java/group/rxcloud/capa/spi/telemetry/CapaReadWriteSpanSpi.java @@ -24,6 +24,14 @@ */ public abstract class CapaReadWriteSpanSpi extends CapaReadWriteSpan { + /** + * Instantiates a new Capa read write span spi. + * + * @param tracerName the tracer name + * @param version the version + * @param schemaUrl the schema url + * @param span the span + */ public CapaReadWriteSpanSpi(String tracerName, String version, String schemaUrl, ReadWriteSpan span) { super(tracerName, version, schemaUrl, span); diff --git a/sdk-spi/src/main/java/group/rxcloud/capa/spi/telemetry/CapaSpanBuilderSpi.java b/sdk-spi/src/main/java/group/rxcloud/capa/spi/telemetry/CapaSpanBuilderSpi.java index f9c3686..5966405 100644 --- a/sdk-spi/src/main/java/group/rxcloud/capa/spi/telemetry/CapaSpanBuilderSpi.java +++ b/sdk-spi/src/main/java/group/rxcloud/capa/spi/telemetry/CapaSpanBuilderSpi.java @@ -24,6 +24,15 @@ */ public abstract class CapaSpanBuilderSpi extends CapaSpanBuilder { + /** + * Instantiates a new Capa span builder spi. + * + * @param tracerName the tracer name + * @param version the version + * @param schemaUrl the schema url + * @param spanName the span name + * @param spanBuilder the span builder + */ public CapaSpanBuilderSpi(String tracerName, String version, String schemaUrl, String spanName, SpanBuilder spanBuilder) { super(tracerName, version, schemaUrl, spanName, spanBuilder); diff --git a/sdk-spi/src/main/java/group/rxcloud/capa/spi/telemetry/CapaTracerBuilderSpi.java b/sdk-spi/src/main/java/group/rxcloud/capa/spi/telemetry/CapaTracerBuilderSpi.java index 3599480..672184b 100644 --- a/sdk-spi/src/main/java/group/rxcloud/capa/spi/telemetry/CapaTracerBuilderSpi.java +++ b/sdk-spi/src/main/java/group/rxcloud/capa/spi/telemetry/CapaTracerBuilderSpi.java @@ -24,6 +24,12 @@ */ public abstract class CapaTracerBuilderSpi extends CapaTracerBuilder { + /** + * Instantiates a new Capa tracer builder spi. + * + * @param tracerName the tracer name + * @param builder the builder + */ public CapaTracerBuilderSpi(String tracerName, TracerBuilder builder) { super(tracerName, builder); } diff --git a/sdk-spi/src/main/java/group/rxcloud/capa/spi/telemetry/CapaTracerSpi.java b/sdk-spi/src/main/java/group/rxcloud/capa/spi/telemetry/CapaTracerSpi.java index 53239a5..8cb2872 100644 --- a/sdk-spi/src/main/java/group/rxcloud/capa/spi/telemetry/CapaTracerSpi.java +++ b/sdk-spi/src/main/java/group/rxcloud/capa/spi/telemetry/CapaTracerSpi.java @@ -24,6 +24,14 @@ */ public abstract class CapaTracerSpi extends CapaTracer { + /** + * Instantiates a new Capa tracer spi. + * + * @param tracerName the tracer name + * @param version the version + * @param schemaUrl the schema url + * @param tracer the tracer + */ public CapaTracerSpi(String tracerName, String version, String schemaUrl, Tracer tracer) { super(tracerName, version, schemaUrl, tracer); } diff --git a/sdk-spi/src/main/java/group/rxcloud/capa/spi/telemetry/ContextPropagatorLoaderSpi.java b/sdk-spi/src/main/java/group/rxcloud/capa/spi/telemetry/ContextPropagatorLoaderSpi.java index 148165d..65f82fe 100644 --- a/sdk-spi/src/main/java/group/rxcloud/capa/spi/telemetry/ContextPropagatorLoaderSpi.java +++ b/sdk-spi/src/main/java/group/rxcloud/capa/spi/telemetry/ContextPropagatorLoaderSpi.java @@ -23,5 +23,4 @@ * SPI context propagator loader. */ public abstract class ContextPropagatorLoaderSpi implements ContextPropagatorLoader { - } diff --git a/sdk-spi/src/test/java/group/rxcloud/capa/spi/http/CapaSerializeHttpSpiTest.java b/sdk-spi/src/test/java/group/rxcloud/capa/spi/http/CapaSerializeHttpSpiTest.java index 5041c86..3136b6e 100644 --- a/sdk-spi/src/test/java/group/rxcloud/capa/spi/http/CapaSerializeHttpSpiTest.java +++ b/sdk-spi/src/test/java/group/rxcloud/capa/spi/http/CapaSerializeHttpSpiTest.java @@ -21,7 +21,7 @@ import group.rxcloud.capa.infrastructure.exceptions.CapaException; import group.rxcloud.capa.infrastructure.serializer.CapaObjectSerializer; import group.rxcloud.capa.infrastructure.serializer.DefaultObjectSerializer; -import group.rxcloud.capa.infrastructure.serializer.ObjectSerializer; +import group.rxcloud.capa.infrastructure.serializer.ExtensionObjectSerializer; import group.rxcloud.capa.spi.http.config.RpcServiceOptions; import group.rxcloud.cloudruntimes.utils.TypeRef; import okhttp3.Headers; @@ -86,7 +86,7 @@ public void testGetRequestBodyWithSerialize_SuccessWhenHeaderHasValue() { Map headers = new HashMap<>(); headers.put("content-type", "application/json"); - RequestBody requestBody = capaSerializeHttpSpi.getRequestBodyWithSerialize("Object", headers); + RequestBody requestBody = capaSerializeHttpSpi.getRequestBodyWithSerialize("Object", "application/json"); String type = requestBody.contentType().type(); String subtype = requestBody.contentType().subtype(); Assertions.assertEquals("application", type); @@ -223,7 +223,7 @@ public void testDoInvokeApi_Success() throws ExecutionException, InterruptedExce /** * serializer/deserializer for request/response objects used in tests only */ - private class TestRuntimeExceptionObjectSerializer extends ObjectSerializer implements CapaObjectSerializer { + private class TestRuntimeExceptionObjectSerializer extends ExtensionObjectSerializer implements CapaObjectSerializer { /** * {@inheritDoc} @@ -254,7 +254,7 @@ public String getContentType() { /** * serializer/deserializer for request/response objects used in tests only */ - private class TestIOExceptionObjectSerializer extends ObjectSerializer implements CapaObjectSerializer { + private class TestIOExceptionObjectSerializer extends ExtensionObjectSerializer implements CapaObjectSerializer { /** * {@inheritDoc} diff --git a/sdk-springboot/pom.xml b/sdk-springboot/pom.xml index 512f725..450be52 100644 --- a/sdk-springboot/pom.xml +++ b/sdk-springboot/pom.xml @@ -23,19 +23,36 @@ capa-parent group.rxcloud - 1.10.10.RELEASE + 1.11.13.2.RELEASE sdk-springboot capa-sdk-springboot - 2.3.12.RELEASE + 2.18.9 + 4.12.0 + 5.3.39 + 2.7.18 1.0.1-RELEASE + + com.fasterxml.jackson + jackson-bom + ${jackson.version} + pom + import + + + org.springframework + spring-framework-bom + ${spring-framework.version} + pom + import + org.springframework.boot spring-boot-dependencies @@ -43,6 +60,11 @@ pom import + + com.squareup.okhttp3 + okhttp + ${okhttp.version} + @@ -56,6 +78,16 @@ org.springframework.boot spring-boot-starter + + + org.springframework.boot + spring-boot-starter-logging + + + org.yaml + snakeyaml + + @@ -66,4 +98,4 @@ - \ No newline at end of file + diff --git a/sdk-springboot/src/main/java/group/rxcloud/capa/springboot/pubsub/PubSubBeanPostProcessor.java b/sdk-springboot/src/main/java/group/rxcloud/capa/springboot/pubsub/PubSubBeanPostProcessor.java index e80c6bf..75f862a 100644 --- a/sdk-springboot/src/main/java/group/rxcloud/capa/springboot/pubsub/PubSubBeanPostProcessor.java +++ b/sdk-springboot/src/main/java/group/rxcloud/capa/springboot/pubsub/PubSubBeanPostProcessor.java @@ -19,11 +19,12 @@ import com.kevinten.vrml.core.beans.SpringContextConfigurator; import com.kevinten.vrml.core.serialization.Serialization; import group.rxcloud.capa.infrastructure.exceptions.CapaException; +import group.rxcloud.capa.infrastructure.serializer.CapaObjectSerializer; import group.rxcloud.capa.infrastructure.serializer.DefaultObjectSerializer; -import group.rxcloud.capa.infrastructure.serializer.ObjectSerializer; import group.rxcloud.capa.pubsub.Topic; import group.rxcloud.capa.pubsub.domain.TopicEventRequest; import group.rxcloud.capa.pubsub.domain.TopicSubscription; +import group.rxcloud.cloudruntimes.utils.TypeRef; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; @@ -49,7 +50,7 @@ public class PubSubBeanPostProcessor implements BeanPostProcessor { private static final Logger logger = LoggerFactory.getLogger(PubSubBeanPostProcessor.class); private final EmbeddedValueResolver embeddedValueResolver; - private final ObjectSerializer serializer; + private final CapaObjectSerializer serializer; PubSubBeanPostProcessor(ConfigurableBeanFactory beanFactory) { embeddedValueResolver = new EmbeddedValueResolver(beanFactory); @@ -84,7 +85,7 @@ public Object postProcessAfterInitialization(Object bean, String beanName) throw * @param clazz Controller class where {@link Topic} is expected. * @param serializer json serializer */ - private static void subscribeToTopics(Class clazz, EmbeddedValueResolver embeddedValueResolver, ObjectSerializer serializer) { + private static void subscribeToTopics(Class clazz, EmbeddedValueResolver embeddedValueResolver, CapaObjectSerializer serializer) { if (clazz == null) { return; } @@ -152,12 +153,12 @@ private static void subscribeToTopics(Class clazz, EmbeddedValueResolver embedde } } - private static Map resolveMetadataMap(ObjectSerializer serializer, String metadata) { + private static Map resolveMetadataMap(CapaObjectSerializer serializer, String metadata) { Map metadataMap = Collections.emptyMap(); if (!StringUtils.isEmpty(metadata)) { byte[] metadataBytes = metadata.getBytes(StandardCharsets.UTF_8); try { - metadataMap = serializer.deserialize(metadataBytes, Map.class); + metadataMap = serializer.deserialize(metadataBytes, TypeRef.get(Map.class)); } catch (Exception e) { if (logger.isErrorEnabled()) { logger.error("[PubSub.@Topic.subscribe] illegal metadata[{}]", diff --git a/sdk/pom.xml b/sdk/pom.xml index 7deb0f7..7bfd1b4 100644 --- a/sdk/pom.xml +++ b/sdk/pom.xml @@ -23,7 +23,7 @@ group.rxcloud capa-parent - 1.10.10.RELEASE + 1.11.13.2.RELEASE capa-sdk diff --git a/sdk/src/main/java/group/rxcloud/capa/rpc/CapaRpcClientBuilder.java b/sdk/src/main/java/group/rxcloud/capa/rpc/CapaRpcClientBuilder.java index e3ef4f0..91ecce5 100644 --- a/sdk/src/main/java/group/rxcloud/capa/rpc/CapaRpcClientBuilder.java +++ b/sdk/src/main/java/group/rxcloud/capa/rpc/CapaRpcClientBuilder.java @@ -17,8 +17,8 @@ package group.rxcloud.capa.rpc; +import group.rxcloud.capa.component.CapaRpcProperties; import group.rxcloud.capa.component.http.CapaHttpBuilder; -import group.rxcloud.capa.infrastructure.CapaProperties; import java.util.function.Supplier; @@ -55,7 +55,7 @@ public CapaRpcClientBuilder(Supplier capaHttpBuilderSupplier) { * Creates a constructor for {@link CapaRpcClient}. */ public CapaRpcClientBuilder(CapaHttpBuilder httpBuilder) { - this.apiProtocol = CapaApiProtocol.parseProtocol(CapaProperties.API_PROTOCOL.get()); + this.apiProtocol = CapaApiProtocol.parseProtocol(CapaRpcProperties.Settings.getApiProtocol()); this.httpBuilder = httpBuilder; } diff --git a/sdk/src/main/java/group/rxcloud/capa/rpc/Controller.java b/sdk/src/main/java/group/rxcloud/capa/rpc/Controller.java new file mode 100644 index 0000000..5667ee9 --- /dev/null +++ b/sdk/src/main/java/group/rxcloud/capa/rpc/Controller.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package group.rxcloud.capa.rpc; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Rpc Server controller. + */ +@Documented +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface Controller { +} diff --git a/sdk/src/main/java/group/rxcloud/capa/rpc/RequestMapping.java b/sdk/src/main/java/group/rxcloud/capa/rpc/RequestMapping.java new file mode 100644 index 0000000..05ef223 --- /dev/null +++ b/sdk/src/main/java/group/rxcloud/capa/rpc/RequestMapping.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package group.rxcloud.capa.rpc; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Rpc Server controller method. + */ +@Documented +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface RequestMapping { +}