├── .editorconfig ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.yaml │ ├── config.yml │ ├── new_feature.yaml │ └── other.yaml ├── release.yml ├── renovate.json └── workflows │ ├── central-sync.yml │ ├── graalvm-dev.yml │ ├── graalvm-latest.yml │ ├── gradle.yml │ ├── publish-snapshot.yml │ └── release.yml ├── .gitignore ├── ISSUE_TEMPLATE.md ├── LICENSE ├── MAINTAINING.md ├── README.md ├── SECURITY.md ├── build-logic ├── build.gradle ├── settings.gradle └── src │ └── main │ └── groovy │ ├── io.micronaut.build.internal.grpc-base.gradle │ ├── io.micronaut.build.internal.grpc-minimal-test.gradle │ ├── io.micronaut.build.internal.grpc-module.gradle │ └── io.micronaut.build.internal.grpc-tests.gradle ├── build.gradle ├── config ├── HEADER ├── checkstyle │ ├── checkstyle.xml │ └── suppressions.xml └── spotless.license.java ├── gradle.properties ├── gradle ├── libs.versions.toml ├── license.gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── grpc-annotation ├── build.gradle └── src │ └── main │ └── java │ └── io │ └── micronaut │ └── grpc │ └── annotation │ ├── GrpcChannel.java │ └── GrpcService.java ├── grpc-bom └── build.gradle ├── grpc-client-runtime ├── build.gradle ├── config │ └── accepted-api-changes.json └── src │ ├── main │ └── java │ │ └── io │ │ └── micronaut │ │ └── grpc │ │ ├── channels │ │ ├── GrpcChannelBuilderFactory.java │ │ ├── GrpcDefaultManagedChannelConfiguration.java │ │ ├── GrpcManagedChannelConfiguration.java │ │ ├── GrpcManagedChannelFactory.java │ │ ├── GrpcNamedManagedChannelConfiguration.java │ │ └── package-info.java │ │ └── discovery │ │ ├── GrpcNameResolverProvider.java │ │ └── package-info.java │ └── test │ ├── groovy │ └── io │ │ └── micronaut │ │ └── grpc │ │ ├── GrpcNamedChannelSpec.groovy │ │ ├── HelloWordGrpcSpec.groovy │ │ ├── ManagedChannelBuilderListener.java │ │ ├── ServerBuilderListener.java │ │ └── discovery │ │ ├── GrpcLoadBalancedServiceSpec.groovy │ │ └── GrpcServiceDiscoverySpec.groovy │ ├── proto │ └── helloworld.proto │ └── resources │ └── logback.xml ├── grpc-health ├── build.gradle └── src │ ├── main │ └── java │ │ └── io │ │ └── micronaut │ │ └── grpc │ │ └── server │ │ └── health │ │ ├── GrpcHealthFactory.java │ │ ├── GrpcServerHealthIndicator.java │ │ └── HealthStatusManagerContainer.java │ └── test │ └── groovy │ └── io │ └── micronaut │ └── grpc │ └── server │ └── health │ ├── GrpcServerHealthIndicatorSpec.groovy │ └── GrpcServerHealthSpec.groovy ├── grpc-opentracing ├── build.gradle └── src │ ├── main │ └── java │ │ └── io │ │ └── micronaut │ │ └── grpc │ │ ├── client │ │ └── tracing │ │ │ ├── GrpcClientTracingInterceptorConfiguration.java │ │ │ ├── GrpcClientTracingInterceptorFactory.java │ │ │ └── package-info.java │ │ └── server │ │ └── tracing │ │ ├── GrpcServerTracingInterceptorConfiguration.java │ │ ├── GrpcServerTracingInterceptorFactory.java │ │ └── package-info.java │ └── test │ ├── groovy │ └── io │ │ └── micronaut │ │ └── grpc │ │ └── server │ │ └── tracing │ │ └── GrpcTracingSpec.groovy │ └── proto │ └── helloworld.proto ├── grpc-runtime └── build.gradle ├── grpc-server-runtime ├── build.gradle ├── config │ └── accepted-api-changes.json └── src │ ├── main │ └── java │ │ └── io │ │ └── micronaut │ │ └── grpc │ │ └── server │ │ ├── GrpcEmbeddedServer.java │ │ ├── GrpcEmbeddedServerListener.java │ │ ├── GrpcServerBuilder.java │ │ ├── GrpcServerChannel.java │ │ ├── GrpcServerConfiguration.java │ │ ├── GrpcServerInstance.java │ │ ├── GrpcSslConfiguration.java │ │ ├── interceptor │ │ └── OrderedServerInterceptor.java │ │ └── package-info.java │ └── test │ ├── groovy │ └── io │ │ └── micronaut │ │ └── grpc │ │ ├── GrpcEmbeddedServerSpec.groovy │ │ ├── GrpcNamedChannelSpec.groovy │ │ ├── GrpcServerConfigurationSpec.groovy │ │ ├── HelloWordGrpcSpec.groovy │ │ ├── ManagedChannelBuilderListener.java │ │ ├── ServerBuilderListener.java │ │ ├── discovery │ │ └── GrpcServiceDiscoverySpec.groovy │ │ └── server │ │ ├── GrpcServerBuilderSpec.groovy │ │ └── interceptor │ │ └── OrderedServerInterceptorSpec.groovy │ ├── proto │ └── helloworld.proto │ └── resources │ ├── example.crt │ ├── example.key │ └── logback.xml ├── protobuff-support ├── build.gradle └── src │ ├── main │ └── java │ │ └── io │ │ └── micronaut │ │ └── protobuf │ │ ├── codec │ │ ├── ExtensionRegistryFactory.java │ │ └── ProtobufferCodec.java │ │ ├── convert │ │ ├── ByteBufToProtoMessageConverter.java │ │ └── ProtoMessageToByteBufConverter.java │ │ └── handler │ │ └── ProtobufBodyHandler.java │ └── test │ ├── groovy │ └── io │ │ └── micronaut │ │ ├── BaseSpec.groovy │ │ ├── ProgrammaticController.groovy │ │ ├── ProgrammaticControllerCreator.groovy │ │ ├── ProgrammaticControllerSpec.groovy │ │ ├── SampleController.groovy │ │ ├── SampleWebsocketClient.groovy │ │ ├── SampleWebsocketHandler.groovy │ │ ├── SimpleHttpGetSpec.groovy │ │ ├── SimpleHttpPostSpec.groovy │ │ └── SimpleWebsocketSpec.groovy │ ├── proto │ └── example.proto │ └── resources │ └── logback-test.xml ├── settings.gradle ├── src └── main │ └── docs │ └── guide │ ├── client.adoc │ ├── gettingStarted.adoc │ ├── introduction.adoc │ ├── protocolBuffers.adoc │ ├── releaseHistory.adoc │ ├── repository.adoc │ ├── server.adoc │ ├── serviceDiscovery.adoc │ ├── toc.yml │ └── tracing.adoc ├── test-suite-groovy ├── build.gradle └── src │ ├── main │ ├── groovy │ │ └── helloworld │ │ │ ├── Application.groovy │ │ │ ├── GreetingEndpoint.groovy │ │ │ ├── GreetingService.groovy │ │ │ └── HealthService.groovy │ ├── proto │ │ └── helloworld.proto │ └── resources │ │ ├── application.yml │ │ └── logback.xml │ └── test │ └── groovy │ └── helloworld │ ├── GreetingEndpointSpec.groovy │ └── HealthCheckSpec.groovy ├── test-suite-java ├── .mvn │ └── wrapper │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── build.gradle ├── pom.xml └── src │ ├── main │ ├── java │ │ └── helloworld │ │ │ ├── Application.java │ │ │ ├── GreetingEndpoint.java │ │ │ ├── GreetingService.java │ │ │ └── HealthService.java │ ├── proto │ │ └── helloworld.proto │ └── resources │ │ ├── application.yml │ │ └── logback.xml │ └── test │ └── java │ └── helloworld │ ├── GreetingEndpointTest.java │ └── HealthCheckTest.java └── test-suite-kotlin ├── build.gradle └── src ├── main ├── kotlin │ └── helloworld │ │ ├── Application.kt │ │ ├── GreetingEndpoint.kt │ │ ├── GreetingService.kt │ │ └── HealthService.kt ├── proto │ └── helloworld.proto └── resources │ ├── META-INF │ └── native-image │ │ └── io.micronaut.grpc │ │ └── kotlin-runtime │ │ └── native-image.properties │ ├── application.yml │ └── logback.xml └── test └── kotlin └── helloworld ├── GreetingServiceTest.kt └── HealthCheckTest.kt /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | trim_trailing_whitespace = true 5 | insert_final_newline = true 6 | charset = utf-8 7 | indent_style = space 8 | 9 | [{*.sh,gradlew}] 10 | end_of_line = lf 11 | 12 | [{*.bat,*.cmd}] 13 | end_of_line = crlf 14 | 15 | [{*.mustache,*.ftl}] 16 | insert_final_newline = false 17 | 18 | [*.java] 19 | indent_size = 4 20 | tab_width = 4 21 | max_line_length = 100 22 | # Import order can be configured with ij_java_imports_layout=... 23 | # See documentation https://youtrack.jetbrains.com/issue/IDEA-170643#focus=streamItem-27-3708697.0-0 24 | 25 | [*.xml] 26 | indent_size = 4 27 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | *.java text eol=lf 5 | *.groovy text eol=lf 6 | *.html text eol=lf 7 | *.kt text eol=lf 8 | *.kts text eol=lf 9 | *.md text diff=markdown eol=lf 10 | *.py text diff=python executable 11 | *.pl text diff=perl executable 12 | *.pm text diff=perl 13 | *.css text diff=css eol=lf 14 | *.js text eol=lf 15 | *.sql text eol=lf 16 | *.q text eol=lf 17 | 18 | *.sh text eol=lf 19 | gradlew text eol=lf 20 | 21 | *.bat text eol=crlf 22 | *.cmd text eol=crlf 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yaml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: File a bug report 3 | body: 4 | - type: markdown 5 | attributes: 6 | value: | 7 | Thanks for reporting an issue, please review the task list below before submitting the issue. Your issue report will be closed if the issue is incomplete and the below tasks not completed. 8 | 9 | NOTE: If you are unsure about something and the issue is more of a question a better place to ask questions is on Github Discussions :arrow_up:, [Stack Overflow](https://stackoverflow.com/tags/micronaut) or [Gitter](https://gitter.im/micronautfw/). 10 | - type: textarea 11 | attributes: 12 | label: Expected Behavior 13 | description: A concise description of what you expected to happen. 14 | placeholder: Tell us what should happen 15 | validations: 16 | required: false 17 | - type: textarea 18 | attributes: 19 | label: Actual Behaviour 20 | description: A concise description of what you're experiencing. 21 | placeholder: Tell us what happens instead 22 | validations: 23 | required: false 24 | - type: textarea 25 | attributes: 26 | label: Steps To Reproduce 27 | description: Steps to reproduce the behavior. 28 | placeholder: | 29 | 1. In this environment... 30 | 2. With this config... 31 | 3. Run '...' 32 | 4. See error... 33 | validations: 34 | required: false 35 | - type: textarea 36 | attributes: 37 | label: Environment Information 38 | description: Environment information where the problem occurs. 39 | placeholder: | 40 | - Operating System: 41 | - JDK Version: 42 | validations: 43 | required: false 44 | - type: input 45 | id: example 46 | attributes: 47 | label: Example Application 48 | description: Example application link. 49 | placeholder: | 50 | Link to GitHub repository with an example that reproduces the issue 51 | validations: 52 | required: false 53 | - type: input 54 | id: version 55 | attributes: 56 | label: Version 57 | description: Micronaut version 58 | validations: 59 | required: true 60 | 61 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | contact_links: 2 | - name: Micronaut Core Discussions 3 | url: https://github.com/micronaut-projects/micronaut-core/discussions 4 | about: Ask questions about Micronaut on Github 5 | - name: Micronaut Data Discussions 6 | url: https://github.com/micronaut-projects/micronaut-data/discussions 7 | about: Ask Micronaut Data related questions on Github 8 | - name: Stack Overflow 9 | url: https://stackoverflow.com/tags/micronaut 10 | about: Ask questions on Stack Overflow 11 | - name: Chat 12 | url: https://gitter.im/micronautfw/ 13 | about: Chat with us on Gitter. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/new_feature.yaml: -------------------------------------------------------------------------------- 1 | name: Feature request 2 | description: Create a new feature request 3 | body: 4 | - type: markdown 5 | attributes: 6 | value: | 7 | Please describe the feature you want for Micronaut to implement, before that check if there is already an existing issue to add it. 8 | - type: textarea 9 | attributes: 10 | label: Feature description 11 | placeholder: Tell us what feature you would like for Micronaut to have and what problem is it going to solve 12 | validations: 13 | required: true 14 | 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/other.yaml: -------------------------------------------------------------------------------- 1 | name: Other 2 | description: Something different 3 | body: 4 | - type: textarea 5 | attributes: 6 | label: Issue description 7 | validations: 8 | required: true 9 | 10 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | changelog: 2 | exclude: 3 | authors: 4 | - micronaut-build 5 | categories: 6 | - title: Breaking Changes 🛠 7 | labels: 8 | - 'type: breaking' 9 | - title: New Features 🎉 10 | labels: 11 | - 'type: enhancement' 12 | - title: Bug Fixes 🐞 13 | labels: 14 | - 'type: bug' 15 | - title: Improvements ⭐ 16 | labels: 17 | - 'type: improvement' 18 | - title: Docs 📖 19 | labels: 20 | - 'type: docs' 21 | - title: Dependency updates 🚀 22 | labels: 23 | - 'type: dependency-upgrade' 24 | - 'dependency-upgrade' 25 | - title: Regressions 🧐 26 | labels: 27 | - 'type: regression' 28 | - title: GraalVM 🏆 29 | labels: 30 | - 'relates-to: graal' 31 | - title: Other Changes 💡 32 | labels: 33 | - "*" 34 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:recommended" 4 | ], 5 | "addLabels": [ 6 | "type: dependency-upgrade" 7 | ], 8 | "schedule": [ 9 | "after 10pm" 10 | ], 11 | "prHourlyLimit": 1, 12 | "prConcurrentLimit": 20, 13 | "timezone": "Europe/Prague", 14 | "packageRules": [ 15 | { 16 | "dependencyDashboardApproval": true, 17 | "matchUpdateTypes": [ 18 | "patch" 19 | ], 20 | "matchCurrentVersion": "!/^0/", 21 | "automerge": true, 22 | "matchPackageNames": [ 23 | "/actions.*/" 24 | ] 25 | }, 26 | { 27 | "matchUpdateTypes": [ 28 | "patch" 29 | ], 30 | "matchCurrentVersion": "!/^0/", 31 | "automerge": true 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /.github/workflows/central-sync.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Do not edit this file directly. Instead, go to: 2 | # 3 | # https://github.com/micronaut-projects/micronaut-project-template/tree/master/.github/workflows 4 | # 5 | # and edit them there. Note that it will be sync'ed to all the Micronaut repos 6 | name: Maven Central Sync 7 | on: 8 | workflow_dispatch: 9 | inputs: 10 | release_version: 11 | description: 'Release version (eg: 1.2.3)' 12 | required: true 13 | jobs: 14 | central-sync: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Checkout repository 18 | uses: actions/checkout@v4 19 | with: 20 | ref: v${{ github.event.inputs.release_version }} 21 | - uses: gradle/wrapper-validation-action@v3 22 | - name: Set up JDK 23 | uses: actions/setup-java@v4 24 | with: 25 | distribution: 'temurin' 26 | java-version: '17' 27 | - name: Publish to Sonatype OSSRH 28 | env: 29 | SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} 30 | SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} 31 | GPG_KEY_ID: ${{ secrets.GPG_KEY_ID }} 32 | GPG_PASSWORD: ${{ secrets.GPG_PASSWORD }} 33 | GPG_FILE: ${{ secrets.GPG_FILE }} 34 | DEVELOCITY_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} 35 | DEVELOCITY_CACHE_USERNAME: ${{ secrets.GRADLE_ENTERPRISE_CACHE_USERNAME }} 36 | DEVELOCITY_CACHE_PASSWORD: ${{ secrets.GRADLE_ENTERPRISE_CACHE_PASSWORD }} 37 | run: | 38 | echo $GPG_FILE | base64 -d > secring.gpg 39 | ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository 40 | -------------------------------------------------------------------------------- /.github/workflows/graalvm-dev.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Do not edit this file directly. Instead, go to: 2 | # 3 | # https://github.com/micronaut-projects/micronaut-project-template/tree/master/.github/workflows 4 | # 5 | # and edit them there. Note that it will be sync'ed to all the Micronaut repos 6 | name: GraalVM Dev CI 7 | on: 8 | schedule: 9 | - cron: "0 1 * * 1-5" # Mon-Fri at 1am UTC 10 | jobs: 11 | build_matrix: 12 | if: github.repository != 'micronaut-projects/micronaut-project-template' 13 | runs-on: ubuntu-latest 14 | env: 15 | DEVELOCITY_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} 16 | DEVELOCITY_CACHE_USERNAME: ${{ secrets.GRADLE_ENTERPRISE_CACHE_USERNAME }} 17 | DEVELOCITY_CACHE_PASSWORD: ${{ secrets.GRADLE_ENTERPRISE_CACHE_PASSWORD }} 18 | outputs: 19 | matrix: ${{ steps.build-matrix.outputs.matrix }} 20 | steps: 21 | - uses: actions/checkout@v4 22 | - name: Build Matrix 23 | uses: micronaut-projects/github-actions/graalvm/build-matrix@master 24 | id: build-matrix 25 | build: 26 | needs: build_matrix 27 | if: github.repository != 'micronaut-projects/micronaut-project-template' 28 | runs-on: ubuntu-latest 29 | strategy: 30 | max-parallel: 6 31 | matrix: 32 | java: ['dev', 'latest-ea'] 33 | distribution: ['graalvm-community', 'graalvm'] 34 | native_test_task: ${{ fromJson(needs.build_matrix.outputs.matrix).native_test_task }} 35 | exclude: 36 | - java: 'dev' 37 | distribution: 'graalvm' 38 | - java: 'latest-ea' 39 | distribution: 'graalvm-community' 40 | env: 41 | DEVELOCITY_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} 42 | DEVELOCITY_CACHE_USERNAME: ${{ secrets.GRADLE_ENTERPRISE_CACHE_USERNAME }} 43 | DEVELOCITY_CACHE_PASSWORD: ${{ secrets.GRADLE_ENTERPRISE_CACHE_PASSWORD }} 44 | steps: 45 | - uses: actions/checkout@v4 46 | - name: Pre-Build Steps 47 | uses: micronaut-projects/github-actions/graalvm/pre-build@master 48 | id: pre-build 49 | with: 50 | java: ${{ matrix.java }} 51 | distribution: ${{ matrix.distribution }} 52 | - name: Build Steps 53 | uses: micronaut-projects/github-actions/graalvm/build@master 54 | id: build 55 | env: 56 | GH_TOKEN_PUBLIC_REPOS_READONLY: ${{ secrets.GH_TOKEN_PUBLIC_REPOS_READONLY }} 57 | GH_USERNAME: ${{ secrets.GH_USERNAME }} 58 | GRAALVM_QUICK_BUILD: true 59 | with: 60 | nativeTestTask: ${{ matrix.native_test_task }} 61 | - name: Post-Build Steps 62 | uses: micronaut-projects/github-actions/graalvm/post-build@master 63 | id: post-build 64 | with: 65 | java: ${{ matrix.java }} 66 | -------------------------------------------------------------------------------- /.github/workflows/graalvm-latest.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Do not edit this file directly. Instead, go to: 2 | # 3 | # https://github.com/micronaut-projects/micronaut-project-template/tree/master/.github/workflows 4 | # 5 | # and edit them there. Note that it will be sync'ed to all the Micronaut repos 6 | name: GraalVM Latest CI 7 | on: 8 | push: 9 | branches: 10 | - master 11 | - '[1-9]+.[0-9]+.x' 12 | pull_request: 13 | branches: 14 | - master 15 | - '[1-9]+.[0-9]+.x' 16 | jobs: 17 | build_matrix: 18 | if: github.repository != 'micronaut-projects/micronaut-project-template' 19 | runs-on: ubuntu-latest 20 | env: 21 | DEVELOCITY_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} 22 | DEVELOCITY_CACHE_USERNAME: ${{ secrets.GRADLE_ENTERPRISE_CACHE_USERNAME }} 23 | DEVELOCITY_CACHE_PASSWORD: ${{ secrets.GRADLE_ENTERPRISE_CACHE_PASSWORD }} 24 | outputs: 25 | matrix: ${{ steps.build-matrix.outputs.matrix }} 26 | steps: 27 | - uses: actions/checkout@v4 28 | - name: Build Matrix 29 | uses: micronaut-projects/github-actions/graalvm/build-matrix@master 30 | id: build-matrix 31 | build: 32 | needs: build_matrix 33 | if: github.repository != 'micronaut-projects/micronaut-project-template' 34 | runs-on: ubuntu-latest 35 | strategy: 36 | max-parallel: 6 37 | matrix: 38 | java: ['17', '21'] 39 | native_test_task: ${{ fromJson(needs.build_matrix.outputs.matrix).native_test_task }} 40 | env: 41 | DEVELOCITY_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} 42 | DEVELOCITY_CACHE_USERNAME: ${{ secrets.GRADLE_ENTERPRISE_CACHE_USERNAME }} 43 | DEVELOCITY_CACHE_PASSWORD: ${{ secrets.GRADLE_ENTERPRISE_CACHE_PASSWORD }} 44 | steps: 45 | - uses: actions/checkout@v4 46 | - name: Pre-Build Steps 47 | uses: micronaut-projects/github-actions/graalvm/pre-build@master 48 | id: pre-build 49 | with: 50 | distribution: 'graalvm' 51 | java: ${{ matrix.java }} 52 | - name: Build Steps 53 | uses: micronaut-projects/github-actions/graalvm/build@master 54 | id: build 55 | env: 56 | GH_TOKEN_PUBLIC_REPOS_READONLY: ${{ secrets.GH_TOKEN_PUBLIC_REPOS_READONLY }} 57 | GH_USERNAME: ${{ secrets.GH_USERNAME }} 58 | GRAALVM_QUICK_BUILD: true 59 | with: 60 | nativeTestTask: ${{ matrix.native_test_task }} 61 | - name: Post-Build Steps 62 | uses: micronaut-projects/github-actions/graalvm/post-build@master 63 | id: post-build 64 | with: 65 | java: ${{ matrix.java }} 66 | -------------------------------------------------------------------------------- /.github/workflows/gradle.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Do not edit this file directly. Instead, go to: 2 | # 3 | # https://github.com/micronaut-projects/micronaut-project-template/tree/master/.github/workflows 4 | # 5 | # and edit them there. Note that it will be sync'ed to all the Micronaut repos 6 | name: Java CI 7 | on: 8 | push: 9 | branches: 10 | - master 11 | - '[1-9]+.[0-9]+.x' 12 | pull_request: 13 | branches: 14 | - master 15 | - '[1-9]+.[0-9]+.x' 16 | jobs: 17 | build: 18 | if: github.repository != 'micronaut-projects/micronaut-project-template' 19 | runs-on: ubuntu-latest 20 | strategy: 21 | matrix: 22 | java: ['17', '21'] 23 | env: 24 | DEVELOCITY_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} 25 | DEVELOCITY_CACHE_USERNAME: ${{ secrets.GRADLE_ENTERPRISE_CACHE_USERNAME }} 26 | DEVELOCITY_CACHE_PASSWORD: ${{ secrets.GRADLE_ENTERPRISE_CACHE_PASSWORD }} 27 | GH_TOKEN_PUBLIC_REPOS_READONLY: ${{ secrets.GH_TOKEN_PUBLIC_REPOS_READONLY }} 28 | GH_USERNAME: ${{ secrets.GH_USERNAME }} 29 | TESTCONTAINERS_RYUK_DISABLED: true 30 | PREDICTIVE_TEST_SELECTION: "${{ github.event_name == 'pull_request' && 'true' || 'false' }}" 31 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 32 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 33 | OSS_INDEX_USERNAME: ${{ secrets.OSS_INDEX_USERNAME }} 34 | OSS_INDEX_PASSWORD: ${{ secrets.OSS_INDEX_PASSWORD }} 35 | steps: 36 | # https://github.com/actions/virtual-environments/issues/709 37 | - name: "🗑 Free disk space" 38 | run: | 39 | sudo rm -rf "/usr/local/share/boost" 40 | sudo rm -rf "$AGENT_TOOLSDIRECTORY" 41 | sudo apt-get clean 42 | df -h 43 | 44 | - name: "📥 Checkout repository" 45 | uses: actions/checkout@v4 46 | with: 47 | fetch-depth: 0 48 | 49 | - name: "🔧 Setup GraalVM CE" 50 | uses: graalvm/setup-graalvm@v1.3.3 51 | with: 52 | distribution: 'graalvm' 53 | java-version: ${{ matrix.java }} 54 | github-token: ${{ secrets.GITHUB_TOKEN }} 55 | 56 | - name: "🔧 Setup Gradle" 57 | uses: gradle/gradle-build-action@v3.5.0 58 | 59 | - name: "❓ Optional setup step" 60 | run: | 61 | [ -f ./setup.sh ] && ./setup.sh || [ ! -f ./setup.sh ] 62 | 63 | - name: "🚔 Sonatype Scan" 64 | id: sonatypescan 65 | run: | 66 | ./gradlew ossIndexAudit --no-parallel --info 67 | 68 | - name: "🛠 Build with Gradle" 69 | id: gradle 70 | run: | 71 | ./gradlew check --no-daemon --continue 72 | 73 | - name: "🔎 Run static analysis" 74 | if: env.SONAR_TOKEN != '' && matrix.java == '17' 75 | run: | 76 | ./gradlew sonar 77 | 78 | - name: "📊 Publish Test Report" 79 | if: always() 80 | uses: mikepenz/action-junit-report@v5 81 | with: 82 | check_name: Java CI / Test Report (${{ matrix.java }}) 83 | report_paths: '**/build/test-results/test/TEST-*.xml' 84 | check_retries: 'true' 85 | 86 | - name: "📜 Upload binary compatibility check results" 87 | if: matrix.java == '17' 88 | uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 89 | with: 90 | name: binary-compatibility-reports 91 | path: "**/build/reports/binary-compatibility-*.html" 92 | 93 | - name: "📦 Publish to Sonatype Snapshots" 94 | if: success() && github.event_name == 'push' && matrix.java == '17' 95 | env: 96 | SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} 97 | SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} 98 | run: ./gradlew publishToSonatype docs --no-daemon 99 | 100 | - name: "❓ Determine docs target repository" 101 | uses: haya14busa/action-cond@v1 102 | id: docs_target 103 | with: 104 | cond: ${{ github.repository == 'micronaut-projects/micronaut-core' }} 105 | if_true: "micronaut-projects/micronaut-docs" 106 | if_false: ${{ github.repository }} 107 | 108 | - name: "📑 Publish to Github Pages" 109 | if: success() && github.event_name == 'push' && matrix.java == '17' 110 | uses: micronaut-projects/github-pages-deploy-action@master 111 | env: 112 | TARGET_REPOSITORY: ${{ steps.docs_target.outputs.value }} 113 | GH_TOKEN: ${{ secrets.GH_TOKEN }} 114 | BRANCH: gh-pages 115 | FOLDER: build/docs 116 | -------------------------------------------------------------------------------- /.github/workflows/publish-snapshot.yml: -------------------------------------------------------------------------------- 1 | # WARNING: Do not edit this file directly. Instead, go to: 2 | # 3 | # https://github.com/micronaut-projects/micronaut-project-template/tree/master/.github/workflows 4 | # 5 | # and edit them there. Note that it will be sync'ed to all the Micronaut repos 6 | name: Publish snapshot release 7 | on: [workflow_dispatch] 8 | jobs: 9 | build: 10 | if: github.repository != 'micronaut-projects/micronaut-project-template' 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - uses: actions/cache@v4 15 | with: 16 | path: ~/.gradle/caches 17 | key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} 18 | restore-keys: | 19 | ${{ runner.os }}-gradle- 20 | - name: Set up JDK 21 | uses: actions/setup-java@v4 22 | with: 23 | distribution: 'temurin' 24 | java-version: '17' 25 | - name: Publish to Sonatype Snapshots 26 | if: success() 27 | env: 28 | SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} 29 | SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} 30 | DEVELOCITY_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} 31 | DEVELOCITY_CACHE_USERNAME: ${{ secrets.GRADLE_ENTERPRISE_CACHE_USERNAME }} 32 | DEVELOCITY_CACHE_PASSWORD: ${{ secrets.GRADLE_ENTERPRISE_CACHE_PASSWORD }} 33 | run: ./gradlew publishToSonatype --no-daemon 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | .DS_Store 3 | target/ 4 | .gradle/ 5 | .idea/ 6 | build/ 7 | !build-logic/src/main/java/io/micronaut/build 8 | classes/ 9 | out/ 10 | *.db 11 | *.log 12 | *.iml 13 | .classpath 14 | .factorypath 15 | bin/ 16 | .settings/ 17 | .project 18 | */test/ 19 | */META-INF/ 20 | *.ipr 21 | *.iws 22 | .kotlintest 23 | */.kotlintest/ 24 | 25 | # ignore resources, are downloaded via a gradle task from micronaut_docs 26 | src/main/docs/resources/css/highlight/*.css 27 | src/main/docs/resources/css/highlight/*.png 28 | src/main/docs/resources/css/highlight/*.jpg 29 | src/main/docs/resources/css/*.css 30 | src/main/docs/resources/js/*.js 31 | src/main/docs/resources/style/*.html 32 | src/main/docs/resources/img/micronaut-logo-white.svg 33 | 34 | # Ignore files generated by test-resources 35 | **/.micronaut/test-resources/ 36 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Thanks for reporting an issue, please review the task list below before submitting the 2 | issue. Your issue report will be closed if the issue is incomplete and the below tasks not completed. 3 | 4 | NOTE: If you are unsure about something and the issue is more of a question a better place to ask questions is on Stack Overflow (https://stackoverflow.com/tags/micronaut) or Gitter (https://gitter.im/micronautfw/). DO NOT use the issue tracker to ask questions. 5 | 6 | ### Task List 7 | 8 | - [ ] Steps to reproduce provided 9 | - [ ] Stacktrace (if present) provided 10 | - [ ] Example that reproduces the problem uploaded to GitHub 11 | - [ ] Full description of the issue provided (see below) 12 | 13 | ### Steps to Reproduce 14 | 15 | 1. TODO 16 | 2. TODO 17 | 3. TODO 18 | 19 | ### Expected Behaviour 20 | 21 | Tell us what should happen 22 | 23 | ### Actual Behaviour 24 | 25 | Tell us what happens instead 26 | 27 | ### Environment Information 28 | 29 | - **Operating System**: TODO 30 | - **Micronaut Version:** TODO 31 | - **JDK Version:** TODO 32 | 33 | ### Example Application 34 | 35 | - TODO: link to GitHub repository with example that reproduces the issue 36 | 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Micronaut GRPC 2 | 3 | [![Maven Central](https://img.shields.io/maven-central/v/io.micronaut.grpc/micronaut-grpc-runtime.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22io.micronaut.grpc%22%20AND%20a:%22micronaut-grpc-runtime%22) 4 | [![Build Status](https://github.com/micronaut-projects/micronaut-grpc/workflows/Java%20CI/badge.svg)](https://github.com/micronaut-projects/micronaut-grpc/actions) 5 | [![Revved up by Develocity](https://img.shields.io/badge/Revved%20up%20by-Develocity-06A0CE?logo=Gradle&labelColor=02303A)](https://ge.micronaut.io/scans) 6 | 7 | This project includes integration between [Micronaut](http://micronaut.io) and [GRPC](https://grpc.io). 8 | 9 | ## Documentation 10 | 11 | See the [Documentation](https://micronaut-projects.github.io/micronaut-grpc/latest/guide) for more information. 12 | 13 | See the [Snapshot Documentation](https://micronaut-projects.github.io/micronaut-grpc/snapshot/guide) for the current development docs. 14 | 15 | ## Examples 16 | 17 | Examples for Java, Kotlin and Groovy can be found in the [examples](https://github.com/micronaut-projects/micronaut-grpc/tree/master/doc-examples) directory. 18 | 19 | ## Snapshots and Releases 20 | 21 | Snaphots are automatically published to [JFrog OSS](https://oss.jfrog.org/artifactory/oss-snapshot-local/) using [Github Actions](https://github.com/micronaut-projects/micronaut-grpc/actions). 22 | 23 | See the documentation in the [Micronaut Docs](https://docs.micronaut.io/latest/guide/index.html#usingsnapshots) for how to configure your build to use snapshots. 24 | 25 | Releases are published to JCenter and Maven Central via [Github Actions](https://github.com/micronaut-projects/micronaut-grpc/actions). 26 | 27 | A release is performed with the following steps: 28 | 29 | * [Edit the version](https://github.com/micronaut-projects/micronaut-grpc/edit/master/gradle.properties) specified by `projectVersion` in `gradle.properties` to a semantic, unreleased version. Example `1.0.0` 30 | * [Create a new release](https://github.com/micronaut-projects/micronaut-grpc/releases/new). The Git Tag should start with `v`. For example `v1.0.0`. 31 | * [Monitor the Workflow](https://github.com/micronaut-projects/micronaut-grpc/actions?query=workflow%3ARelease) to check it passed successfully. 32 | * Celebrate! -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | We release patches for security vulnerabilities. Which versions are eligible 4 | receiving such patches depend on the CVSS v3.0 Rating: 5 | 6 | | CVSS v3.0 | Supported Versions | 7 | |-----------|-------------------------------------------| 8 | | 9.0-10.0 | Releases within the previous three months | 9 | | 4.0-8.9 | Most recent release | 10 | 11 | ## Reporting a Vulnerability 12 | 13 | Please responsibly disclose (suspected) security vulnerabilities to 14 | **[The Micronaut Foundation](foundation@micronaut.io)**. You will receive a response from 15 | us within 48 hours. If the issue is confirmed, we will release a patch as soon 16 | as possible depending on complexity but historically within a few days. 17 | -------------------------------------------------------------------------------- /build-logic/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'groovy-gradle-plugin' 3 | } 4 | 5 | repositories { 6 | gradlePluginPortal() 7 | mavenCentral() 8 | } 9 | 10 | dependencies { 11 | implementation(libs.micronaut.gradle.plugin) 12 | implementation(libs.sonatype.scan) 13 | } 14 | -------------------------------------------------------------------------------- /build-logic/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'build-logic' 2 | 3 | dependencyResolutionManagement { 4 | versionCatalogs { 5 | libs { 6 | from(files("../gradle/libs.versions.toml")) 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /build-logic/src/main/groovy/io.micronaut.build.internal.grpc-base.gradle: -------------------------------------------------------------------------------- 1 | repositories { 2 | mavenCentral() 3 | } 4 | 5 | tasks.withType(Test) { 6 | testLogging { 7 | showStandardStreams = true 8 | exceptionFormat = 'full' 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /build-logic/src/main/groovy/io.micronaut.build.internal.grpc-minimal-test.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'io.micronaut.build.internal.grpc-base' 3 | id 'io.micronaut.minimal.application' 4 | } 5 | -------------------------------------------------------------------------------- /build-logic/src/main/groovy/io.micronaut.build.internal.grpc-module.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'io.micronaut.build.internal.grpc-base' 3 | id "io.micronaut.build.internal.module" 4 | id("org.sonatype.gradle.plugins.scan") 5 | } 6 | String ossIndexUsername = System.getenv("OSS_INDEX_USERNAME") ?: project.properties["ossIndexUsername"] 7 | String ossIndexPassword = System.getenv("OSS_INDEX_PASSWORD") ?: project.properties["ossIndexPassword"] 8 | boolean sonatypePluginConfigured = ossIndexUsername != null && ossIndexPassword != null 9 | ossIndexAudit { 10 | if (sonatypePluginConfigured) { 11 | username = ossIndexUsername 12 | password = ossIndexPassword 13 | } 14 | excludeCompileOnly = true 15 | } 16 | -------------------------------------------------------------------------------- /build-logic/src/main/groovy/io.micronaut.build.internal.grpc-tests.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "io.micronaut.build.internal.grpc-base" 3 | id 'org.graalvm.buildtools.native' 4 | id "io.micronaut.application" 5 | } 6 | 7 | tasks.named("test") { 8 | useJUnitPlatform() 9 | } 10 | 11 | graalvmNative { 12 | toolchainDetection = false 13 | metadataRepository { 14 | enabled = true 15 | } 16 | binaries { 17 | all { 18 | resources.autodetect() 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "io.micronaut.build.internal.docs" 3 | id "io.micronaut.build.internal.quality-reporting" 4 | } 5 | 6 | repositories { 7 | mavenCentral() 8 | } 9 | 10 | configurations.all { 11 | resolutionStrategy { 12 | preferProjectModules() 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /config/HEADER: -------------------------------------------------------------------------------- 1 | Copyright ${year} original authors 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | https://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /config/checkstyle/suppressions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /config/spotless.license.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-$YEAR original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | projectVersion=4.10.0-SNAPSHOT 2 | projectGroup=io.micronaut.grpc 3 | title=Micronaut gRPC 4 | projectDesc=Integration between Micronaut and gRPC 5 | projectUrl=https://micronaut.io 6 | githubSlug=micronaut-projects/micronaut-grpc 7 | developers=Graeme Rocher 8 | 9 | org.gradle.caching=true 10 | # For sonarqube 11 | org.gradle.jvmargs=-XX:MaxMetaspaceSize=1g 12 | 13 | # No matter which Java toolchain we use, the Kotlin Daemon is always invoked by the current JDK. 14 | # Therefor to fix Kapt errors when running tests under Java 21, we need to open up some modules for the Kotlin Daemon. 15 | kotlin.daemon.jvmargs=--add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED\ 16 | --add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED \ 17 | --add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED \ 18 | --add-opens=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED \ 19 | --add-opens=jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED \ 20 | --add-opens=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED \ 21 | --add-opens=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED \ 22 | --add-opens=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED \ 23 | --add-opens=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \ 24 | --add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED 25 | -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | managed-grpc = '1.69.1' 3 | managed-protobuf = '3.25.7' 4 | managed-protobuf-gradle = '0.9.4' 5 | managed-grpc-kotlin = '1.4.3' 6 | 7 | jackson-datatype-protobuf = '0.9.17' 8 | jaeger-core = '1.8.1' 9 | opentracing-grpc = '0.2.3' 10 | opentracing-mock = '0.33.0' 11 | graal-svm = '24.2.1' 12 | javax-annotation-api = '1.3.2' 13 | 14 | micronaut-docs = '2.0.0' 15 | micronaut-gradle-plugin = '4.5.3' 16 | micronaut-tracing-legacy = '3.2.7' 17 | micronaut = "4.8.14" 18 | micronaut-platform = "4.7.6" 19 | micronaut-discovery-client = "4.6.0" 20 | micronaut-reactor = "3.7.0" 21 | micronaut-tracing = "6.9.0" 22 | micronaut-validation = "4.9.0" 23 | micronaut-kotlin = "4.6.0" 24 | micronaut-logging = "1.6.1" 25 | micronaut-test = "4.7.0" 26 | sonatype-scan = "3.0.0" 27 | 28 | [libraries] 29 | # Managed 30 | managed-grpc-core = { module = 'io.grpc:grpc-core', version.ref = 'managed-grpc' } 31 | managed-grpc-protobuf = { module = 'io.grpc:grpc-protobuf', version.ref = 'managed-grpc' } 32 | managed-grpc-stub = { module = 'io.grpc:grpc-stub', version.ref = 'managed-grpc' } 33 | managed-grpc-services = { module = 'io.grpc:grpc-services', version.ref = 'managed-grpc' } 34 | managed-protoc-grpc = { module = 'io.grpc:protoc-gen-grpc-java', version.ref = 'managed-grpc' } 35 | managed-grpc-kotlin-stub = { module = 'io.grpc:grpc-kotlin-stub', version.ref = 'managed-grpc-kotlin' } 36 | 37 | managed-protobuf-java = { module = 'com.google.protobuf:protobuf-java', version.ref = 'managed-protobuf' } 38 | managed-protobuf-javalite = { module = 'com.google.protobuf:protobuf-javalite', version.ref = 'managed-protobuf' } 39 | managed-protobuf-kotlin = { module = 'com.google.protobuf:protobuf-kotlin', version.ref = 'managed-protobuf' } 40 | managed-protobuf-kotlin-lite = { module = 'com.google.protobuf:protobuf-kotlin-lite', version.ref = 'managed-protobuf' } 41 | managed-protobuf-java-util = { module = 'com.google.protobuf:protobuf-java-util', version.ref = 'managed-protobuf' } 42 | managed-protoc = { module = 'com.google.protobuf:protoc', version.ref = 'managed-protobuf' } 43 | 44 | ## BOMs 45 | boms-protobuf = { module = 'com.google.protobuf:protobuf-bom', version.ref = 'managed-protobuf' } 46 | boms-grpc = { module = 'io.grpc:grpc-bom', version.ref = 'managed-grpc' } 47 | 48 | # Micronaut 49 | micronaut-core = { module = 'io.micronaut:micronaut-core-bom', version.ref = 'micronaut' } 50 | micronaut-docs-asciidoc-config-props = { module = 'io.micronaut.docs:micronaut-docs-asciidoc-config-props', version.ref = 'micronaut-docs' } 51 | micronaut-tracing-legacy = { module = 'io.micronaut:micronaut-tracing', version.ref = 'micronaut-tracing-legacy' } 52 | micronaut-reactor = { module = 'io.micronaut.reactor:micronaut-reactor-bom', version.ref = 'micronaut-reactor' } 53 | micronaut-tracing = { module = 'io.micronaut.tracing:micronaut-tracing-bom', version.ref = 'micronaut-tracing' } 54 | micronaut-discovery-client = { module = "io.micronaut.discovery:micronaut-discovery-client", version.ref = "micronaut-discovery-client" } 55 | micronaut-validation = { module = 'io.micronaut.validation:micronaut-validation-bom', version.ref = 'micronaut-validation' } 56 | micronaut-kotlin = { module = 'io.micronaut.kotlin:micronaut-kotlin-bom', version.ref = 'micronaut-kotlin' } 57 | micronaut-logging = { module = 'io.micronaut.logging:micronaut-logging-bom', version.ref = 'micronaut-logging' } 58 | micronaut-test = { module = 'io.micronaut.test:micronaut-test-bom', version.ref = 'micronaut-test' } 59 | micronaut-gradle-plugin = { module = 'io.micronaut.gradle:micronaut-gradle-plugin', version.ref = 'micronaut-gradle-plugin' } 60 | 61 | # Other 62 | graal-svm = { module = 'org.graalvm.nativeimage:svm', version.ref = 'graal-svm' } 63 | grpc-netty = { module = 'io.grpc:grpc-netty' } 64 | 65 | jackson-datatype-protobuf = { module = 'com.hubspot.jackson:jackson-datatype-protobuf', version.ref = 'jackson-datatype-protobuf' } 66 | javax-annotation-api = { module = 'javax.annotation:javax.annotation-api', version.ref = 'javax-annotation-api' } 67 | 68 | jaeger-core = { module = 'io.jaegertracing:jaeger-core', version.ref = 'jaeger-core' } 69 | 70 | netty-tcnative-boringssl-static = { module = 'io.netty:netty-tcnative-boringssl-static' } 71 | 72 | opentracing-grpc = { module = 'io.opentracing.contrib:opentracing-grpc', version.ref = 'opentracing-grpc' } 73 | opentracing-mock = { module = 'io.opentracing:opentracing-mock', version.ref = 'opentracing-mock' } 74 | sonatype-scan = { module = "org.sonatype.gradle.plugins:scan-gradle-plugin", version.ref = "sonatype-scan" } 75 | [plugins] 76 | protobuf = { id = 'com.google.protobuf', version.ref = 'managed-protobuf-gradle' } 77 | -------------------------------------------------------------------------------- /gradle/license.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.github.hierynomus.license' 2 | 3 | license { 4 | header = rootProject.file('config/HEADER') 5 | strictCheck = true 6 | ignoreFailures = true 7 | mapping { 8 | kt = 'SLASHSTAR_STYLE' 9 | java = 'SLASHSTAR_STYLE' 10 | groovy = 'SLASHSTAR_STYLE' 11 | } 12 | ext.year = '2017-2022' 13 | 14 | exclude "**/transaction/**" 15 | exclude '**/*.txt' 16 | exclude '**/*.html' 17 | exclude '**/*.xml' 18 | exclude '**/*.json' 19 | exclude '**/build-info.properties' 20 | exclude '**/git.properties' 21 | exclude '**/othergit.properties' 22 | } 23 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/micronaut-projects/micronaut-grpc/165833157056e97eb0313098a3c2ac7b115cc0ad/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /grpc-annotation/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'io.micronaut.build.internal.grpc-module' 3 | } 4 | -------------------------------------------------------------------------------- /grpc-annotation/src/main/java/io/micronaut/grpc/annotation/GrpcChannel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.micronaut.grpc.annotation; 17 | 18 | import java.lang.annotation.Documented; 19 | import java.lang.annotation.Retention; 20 | 21 | import io.micronaut.context.annotation.AliasFor; 22 | 23 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 24 | 25 | /** 26 | * An annotation that can be used to inject a GRPC {@code ManagedChannel}. 27 | * 28 | * @author graemerocher 29 | * @since 1.0 30 | */ 31 | @Documented 32 | @Retention(RUNTIME) 33 | public @interface GrpcChannel { 34 | 35 | /** 36 | * @return The URL or service ID of the remote service 37 | */ 38 | @AliasFor(member = "id") 39 | String value() default ""; 40 | 41 | /** 42 | * @return The ID of the client 43 | */ 44 | @AliasFor(member = "value") 45 | String id() default ""; 46 | } 47 | -------------------------------------------------------------------------------- /grpc-annotation/src/main/java/io/micronaut/grpc/annotation/GrpcService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.micronaut.grpc.annotation; 17 | 18 | import java.lang.annotation.Documented; 19 | import java.lang.annotation.ElementType; 20 | import java.lang.annotation.Retention; 21 | import java.lang.annotation.Target; 22 | 23 | import jakarta.inject.Singleton; 24 | 25 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 26 | 27 | /** 28 | * A meta annotation for annotation GRPC services. Note that annotation is more 29 | * for documentation purposes and not strictly necessary. 30 | * 31 | * @author graemerocher 32 | * @since 1.0 33 | */ 34 | @Documented 35 | @Retention(RUNTIME) 36 | @Singleton 37 | @Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE}) 38 | public @interface GrpcService { 39 | } 40 | -------------------------------------------------------------------------------- /grpc-bom/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "io.micronaut.build.internal.bom" 3 | } 4 | -------------------------------------------------------------------------------- /grpc-client-runtime/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'io.micronaut.build.internal.grpc-module' 3 | alias libs.plugins.protobuf 4 | } 5 | 6 | dependencies { 7 | api platform(libs.boms.grpc) 8 | api projects.micronautGrpcAnnotation 9 | api projects.micronautGrpcOpentracing 10 | api libs.managed.grpc.core 11 | api libs.managed.grpc.protobuf 12 | api libs.managed.grpc.stub 13 | 14 | compileOnly mn.micronaut.discovery.core 15 | 16 | implementation libs.grpc.netty 17 | implementation mn.reactor 18 | implementation mn.micronaut.buffer.netty 19 | 20 | testCompileOnly libs.javax.annotation.api 21 | testCompileOnly libs.managed.protobuf.java 22 | 23 | testImplementation projects.micronautGrpcServerRuntime 24 | testImplementation mn.micronaut.discovery.core 25 | testImplementation mnReactor.micronaut.reactor.http.client 26 | testImplementation mn.micronaut.jackson.databind 27 | testImplementation libs.opentracing.mock 28 | 29 | testRuntimeOnly mn.micronaut.jackson.databind 30 | } 31 | 32 | // compileJava.options.fork=true 33 | // compileJava.options.forkOptions.jvmArgs = ['-Xdebug', '-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005'] 34 | 35 | sourceSets { 36 | test { 37 | java { 38 | srcDirs 'build/generated/source/proto/test/grpc' 39 | srcDirs 'build/generated/source/proto/test/java' 40 | } 41 | } 42 | } 43 | 44 | protobuf { 45 | protoc { artifact = libs.managed.protoc.asProvider().get() } 46 | plugins { 47 | grpc { artifact = libs.managed.protoc.grpc.get() } 48 | } 49 | generateProtoTasks { 50 | all()*.plugins { grpc {} } 51 | } 52 | } 53 | 54 | micronautBuild { 55 | binaryCompatibility { 56 | acceptedRegressionsFile = file("config/accepted-api-changes.json") 57 | } 58 | } 59 | 60 | -------------------------------------------------------------------------------- /grpc-client-runtime/src/main/java/io/micronaut/grpc/channels/GrpcChannelBuilderFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.micronaut.grpc.channels; 17 | 18 | import java.util.Collections; 19 | import java.util.List; 20 | import java.util.concurrent.ExecutorService; 21 | 22 | import io.grpc.ClientInterceptor; 23 | import io.grpc.netty.NettyChannelBuilder; 24 | import io.micronaut.context.ApplicationContext; 25 | import io.micronaut.context.annotation.Bean; 26 | import io.micronaut.context.annotation.Factory; 27 | import io.micronaut.context.annotation.Parameter; 28 | import io.micronaut.context.annotation.Prototype; 29 | import io.micronaut.core.util.CollectionUtils; 30 | import io.micronaut.inject.qualifiers.Qualifiers; 31 | import io.micronaut.scheduling.TaskExecutors; 32 | 33 | import jakarta.inject.Named; 34 | 35 | /** 36 | * Factory class for creating {@link NettyChannelBuilder} instances. 37 | * 38 | * @author graemerocher 39 | * @since 1.0 40 | */ 41 | @Factory 42 | public class GrpcChannelBuilderFactory { 43 | 44 | private final ApplicationContext beanContext; 45 | private final ExecutorService executorService; 46 | 47 | /** 48 | * Default constructor. 49 | * 50 | * @param beanContext The bean context 51 | * @param executorService The I/O executor service 52 | */ 53 | public GrpcChannelBuilderFactory( 54 | ApplicationContext beanContext, 55 | @Named(TaskExecutors.IO) ExecutorService executorService) { 56 | this.beanContext = beanContext; 57 | this.executorService = executorService; 58 | } 59 | 60 | /** 61 | * Constructor a managed channel build for the given target name and interceptors. 62 | * 63 | * @param target The target name 64 | * @param interceptors The interceptors 65 | * 66 | * @return The channel builder 67 | */ 68 | @Bean 69 | @Prototype 70 | protected NettyChannelBuilder managedChannelBuilder(@Parameter String target, List interceptors) { 71 | GrpcManagedChannelConfiguration config = beanContext.findBean(GrpcManagedChannelConfiguration.class, Qualifiers.byName(target)).orElseGet(() -> { 72 | final GrpcDefaultManagedChannelConfiguration mcc = new GrpcDefaultManagedChannelConfiguration( 73 | target, 74 | beanContext.getEnvironment(), 75 | executorService 76 | ); 77 | beanContext.inject(mcc); 78 | return mcc; 79 | } 80 | ); 81 | final NettyChannelBuilder channelBuilder = config.getChannelBuilder(); 82 | if (CollectionUtils.isNotEmpty(interceptors)) { 83 | Collections.reverse(interceptors); 84 | channelBuilder.intercept(interceptors); 85 | } 86 | return channelBuilder; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /grpc-client-runtime/src/main/java/io/micronaut/grpc/channels/GrpcDefaultManagedChannelConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.micronaut.grpc.channels; 17 | 18 | import java.util.concurrent.ExecutorService; 19 | 20 | import io.micronaut.context.annotation.ConfigurationProperties; 21 | import io.micronaut.context.env.Environment; 22 | import io.micronaut.scheduling.TaskExecutors; 23 | 24 | import jakarta.inject.Named; 25 | 26 | /** 27 | * Default configuration for all GRPC clients. 28 | * 29 | * @author graemerocher 30 | * @since 1.0 31 | */ 32 | @ConfigurationProperties(GrpcDefaultManagedChannelConfiguration.PREFIX) 33 | public class GrpcDefaultManagedChannelConfiguration extends GrpcManagedChannelConfiguration { 34 | 35 | public static final String NAME = "default"; 36 | public static final String PREFIX = "grpc.client"; 37 | 38 | /** 39 | * Default constructor. 40 | * 41 | * @param env The environment 42 | * @param executorService The executor service 43 | */ 44 | public GrpcDefaultManagedChannelConfiguration( 45 | Environment env, 46 | @Named(TaskExecutors.IO) ExecutorService executorService) { 47 | super(NAME, env, executorService); 48 | } 49 | 50 | /** 51 | * Default constructor. 52 | * 53 | * @param target The target 54 | * @param env The environment 55 | * @param executorService The executor service 56 | */ 57 | public GrpcDefaultManagedChannelConfiguration( 58 | String target, 59 | Environment env, 60 | @Named(TaskExecutors.IO) ExecutorService executorService) { 61 | super(target, env, executorService); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /grpc-client-runtime/src/main/java/io/micronaut/grpc/channels/GrpcNamedManagedChannelConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.micronaut.grpc.channels; 17 | 18 | import java.util.concurrent.ExecutorService; 19 | 20 | import io.micronaut.context.annotation.EachProperty; 21 | import io.micronaut.context.annotation.Parameter; 22 | import io.micronaut.context.env.Environment; 23 | import io.micronaut.scheduling.TaskExecutors; 24 | 25 | import jakarta.inject.Named; 26 | 27 | /** 28 | * Constructs a named channel configuration for each property specified in {@link GrpcManagedChannelConfiguration#PREFIX}. 29 | * 30 | * @author graemerocher 31 | * @since 1.0.0 32 | */ 33 | @EachProperty(GrpcManagedChannelConfiguration.PREFIX) 34 | public class GrpcNamedManagedChannelConfiguration extends GrpcManagedChannelConfiguration { 35 | 36 | /** 37 | * Default constructor. 38 | * 39 | * @param name The name 40 | * @param env The environment 41 | * @param executorService The executor service 42 | */ 43 | public GrpcNamedManagedChannelConfiguration( 44 | @Parameter String name, 45 | Environment env, 46 | @Named(TaskExecutors.IO) ExecutorService executorService) { 47 | super(name, env, executorService); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /grpc-client-runtime/src/main/java/io/micronaut/grpc/channels/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** 17 | * Classes related to GRPC channels and clients. 18 | */ 19 | package io.micronaut.grpc.channels; 20 | -------------------------------------------------------------------------------- /grpc-client-runtime/src/main/java/io/micronaut/grpc/discovery/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** 17 | * Classes related to GRPC service discovery. 18 | */ 19 | package io.micronaut.grpc.discovery; 20 | -------------------------------------------------------------------------------- /grpc-client-runtime/src/test/groovy/io/micronaut/grpc/ManagedChannelBuilderListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.micronaut.grpc; 17 | 18 | import io.grpc.ManagedChannelBuilder; 19 | import io.micronaut.context.event.BeanCreatedEvent; 20 | import io.micronaut.context.event.BeanCreatedEventListener; 21 | 22 | import jakarta.inject.Singleton; 23 | 24 | @Singleton 25 | public class ManagedChannelBuilderListener implements BeanCreatedEventListener> { 26 | 27 | @Override 28 | public ManagedChannelBuilder onCreated(BeanCreatedEvent> event) { 29 | final ManagedChannelBuilder channelBuilder = event.getBean(); 30 | channelBuilder.maxInboundMessageSize(1024); 31 | return channelBuilder; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /grpc-client-runtime/src/test/groovy/io/micronaut/grpc/ServerBuilderListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.micronaut.grpc; 17 | 18 | import io.grpc.ServerBuilder; 19 | import io.micronaut.context.event.BeanCreatedEvent; 20 | import io.micronaut.context.event.BeanCreatedEventListener; 21 | 22 | import jakarta.inject.Singleton; 23 | 24 | @Singleton 25 | public class ServerBuilderListener implements BeanCreatedEventListener> { 26 | 27 | @Override 28 | public ServerBuilder onCreated(BeanCreatedEvent> event) { 29 | final ServerBuilder builder = event.getBean(); 30 | builder.maxInboundMessageSize(1024); 31 | return builder; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /grpc-client-runtime/src/test/groovy/io/micronaut/grpc/discovery/GrpcLoadBalancedServiceSpec.groovy: -------------------------------------------------------------------------------- 1 | package io.micronaut.grpc.discovery 2 | 3 | import io.grpc.ManagedChannel 4 | import io.grpc.examples.helloworld.HelloReply 5 | import io.grpc.examples.helloworld.HelloRequest 6 | import io.grpc.examples.helloworld.MultiNodeGreeterGrpc 7 | import io.grpc.stub.StreamObserver 8 | import io.micronaut.context.ApplicationContext 9 | import io.micronaut.context.annotation.Factory 10 | import io.micronaut.context.annotation.Requires 11 | import io.micronaut.context.annotation.Value 12 | import io.micronaut.core.io.socket.SocketUtils 13 | import io.micronaut.grpc.annotation.GrpcChannel 14 | import io.micronaut.runtime.server.EmbeddedServer 15 | import jakarta.inject.Singleton 16 | import spock.lang.Specification 17 | 18 | class GrpcLoadBalancedServiceSpec extends Specification { 19 | 20 | void "test GRPC named service discovery with round robin load balancing"() { 21 | 22 | given: "A service is run on multiple servers" 23 | def port1 = SocketUtils.findAvailableTcpPort() 24 | def port2 = SocketUtils.findAvailableTcpPort() 25 | def port3 = SocketUtils.findAvailableTcpPort() 26 | 27 | EmbeddedServer server1 = ApplicationContext.run(EmbeddedServer, [ 28 | 'spec.name': 'GrpcLoadBalancedServiceSpec-Server', 29 | 'micronaut.application.name': 'greet', 30 | 'grpc.server.port' : port1 31 | ]) 32 | 33 | EmbeddedServer server2 = ApplicationContext.run(EmbeddedServer, [ 34 | 'spec.name': 'GrpcLoadBalancedServiceSpec-Server', 35 | 'micronaut.application.name': 'greet', 36 | 'grpc.server.port' : port2 37 | ]) 38 | 39 | EmbeddedServer server3 = ApplicationContext.run(EmbeddedServer, [ 40 | 'spec.name': 'GrpcLoadBalancedServiceSpec-Server', 41 | 'micronaut.application.name': 'greet', 42 | 'grpc.server.port' : port3 43 | ]) 44 | 45 | and: 'then a client is run that declares the service' 46 | ApplicationContext client = ApplicationContext.run([ 47 | 'spec.name': 'GrpcLoadBalancedServiceSpec-Client', 48 | (GrpcNameResolverProvider.ENABLED) : true, 49 | 'grpc.channels.greet.plaintext' : true, 50 | 'grpc.channels.greet.default-load-balancing-policy' : 'round_robin', 51 | 'micronaut.http.services.greet.urls[0]': server1.URL.toString(), 52 | 'micronaut.http.services.greet.urls[1]': server2.URL.toString(), 53 | 'micronaut.http.services.greet.urls[2]': server3.URL.toString() 54 | ]) 55 | 56 | when: 'the service is called many times' 57 | MultiNodeGreeterGrpc.MultiNodeGreeterFutureStub stub = client.getBean(MultiNodeGreeterGrpc.MultiNodeGreeterFutureStub) 58 | Set results = new HashSet<>() 59 | for (int i=0; i<12; i++) { 60 | results.add(stub.sayHello(HelloRequest.newBuilder().setName("test").build()).get().message) 61 | } 62 | 63 | then: 'the calls are load balanced across the 3 different servers' 64 | results.size() == 3 65 | 66 | cleanup: 67 | client.stop() 68 | server1.stop() 69 | server2.stop() 70 | server3.stop() 71 | } 72 | 73 | @Requires(property = "spec.name", value = "GrpcLoadBalancedServiceSpec-Client") 74 | @Factory 75 | static class Clients { 76 | @Singleton 77 | MultiNodeGreeterGrpc.MultiNodeGreeterFutureStub futureStub(@GrpcChannel("greet") ManagedChannel channel) { 78 | MultiNodeGreeterGrpc.newFutureStub( 79 | channel 80 | ) 81 | } 82 | } 83 | 84 | @Singleton 85 | @Requires(property = "spec.name", value = "GrpcLoadBalancedServiceSpec-Server") 86 | static class MultiNodeGreeterImpl extends MultiNodeGreeterGrpc.MultiNodeGreeterImplBase { 87 | 88 | @Value('${grpc.server.port}') 89 | Integer port 90 | 91 | @Override 92 | void sayHello(HelloRequest request, StreamObserver responseObserver) { 93 | HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + request.getName() + " from " + port).build() 94 | responseObserver.onNext(reply) 95 | responseObserver.onCompleted() 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /grpc-client-runtime/src/test/proto/helloworld.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The gRPC Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | syntax = "proto3"; 15 | 16 | option java_multiple_files = true; 17 | option java_package = "io.grpc.examples.helloworld"; 18 | option java_outer_classname = "HelloWorldProto"; 19 | option objc_class_prefix = "HLW"; 20 | 21 | package helloworld; 22 | 23 | // The greeting service definition. 24 | service Greeter { 25 | // Sends a greeting 26 | rpc SayHello (HelloRequest) returns (HelloReply) {} 27 | } 28 | 29 | // The greeting service definition. 30 | service Greeter2 { 31 | // Sends a greeting 32 | rpc SayHello (HelloRequest) returns (HelloReply) {} 33 | } 34 | 35 | // Multi-node greeting service definition 36 | service MultiNodeGreeter { 37 | rpc SayHello (HelloRequest) returns (HelloReply) {} 38 | } 39 | 40 | // The request message containing the user's name. 41 | message HelloRequest { 42 | string name = 1; 43 | } 44 | 45 | // The response message containing the greetings 46 | message HelloReply { 47 | string message = 1; 48 | } 49 | -------------------------------------------------------------------------------- /grpc-client-runtime/src/test/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /grpc-health/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'io.micronaut.build.internal.grpc-module' 3 | alias libs.plugins.protobuf 4 | } 5 | 6 | dependencies { 7 | 8 | api platform(libs.boms.grpc) 9 | api projects.micronautGrpcAnnotation 10 | api libs.managed.grpc.core 11 | api libs.managed.grpc.protobuf 12 | api libs.managed.grpc.stub 13 | 14 | // change these to "api" dependencies when this module 15 | // is removed as a dependency of "grpc-server-runtime" 16 | compileOnly libs.managed.grpc.services 17 | compileOnly mn.micronaut.management 18 | 19 | testImplementation libs.managed.grpc.services 20 | testImplementation mn.micronaut.management 21 | testImplementation mnReactor.micronaut.reactor.http.client 22 | testImplementation projects.micronautGrpcServerRuntime 23 | } 24 | 25 | protobuf { 26 | protoc { artifact = libs.managed.protoc.asProvider().get() } 27 | plugins { 28 | grpc { artifact = libs.managed.protoc.grpc.get() } 29 | } 30 | generateProtoTasks { 31 | all()*.plugins { grpc {} } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /grpc-health/src/main/java/io/micronaut/grpc/server/health/GrpcHealthFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2022 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.micronaut.grpc.server.health; 17 | 18 | import io.grpc.protobuf.services.HealthStatusManager; 19 | import io.micronaut.context.annotation.Factory; 20 | import io.micronaut.context.annotation.Requires; 21 | import io.micronaut.core.util.StringUtils; 22 | 23 | import jakarta.inject.Singleton; 24 | 25 | /** 26 | * @since 3.3.0 27 | */ 28 | @Factory 29 | public class GrpcHealthFactory { 30 | 31 | public static final String HEALTH_ENABLED = "grpc.server.health.enabled"; 32 | 33 | /** 34 | * Creates a {@link HealthStatusManager} bean if GRPC health is enabled. 35 | * 36 | * @return The Singleton{@link HealthStatusManager} bean. 37 | */ 38 | @Singleton 39 | @Requires(property = GrpcHealthFactory.HEALTH_ENABLED, value = StringUtils.TRUE, defaultValue = StringUtils.TRUE) 40 | @Requires(classes = HealthStatusManager.class) 41 | public HealthStatusManager healthStatusManager() { 42 | return new HealthStatusManager(); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /grpc-health/src/main/java/io/micronaut/grpc/server/health/GrpcServerHealthIndicator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.micronaut.grpc.server.health; 17 | 18 | import java.util.Map; 19 | 20 | import io.micronaut.context.annotation.Requires; 21 | import io.micronaut.core.async.publisher.AsyncSingleResultPublisher; 22 | import io.micronaut.core.util.StringUtils; 23 | import io.micronaut.health.HealthStatus; 24 | import io.micronaut.management.endpoint.health.HealthEndpoint; 25 | import io.micronaut.management.health.indicator.HealthIndicator; 26 | import io.micronaut.management.health.indicator.HealthResult; 27 | import io.micronaut.runtime.server.EmbeddedServer; 28 | 29 | import org.reactivestreams.Publisher; 30 | 31 | import jakarta.inject.Named; 32 | import jakarta.inject.Singleton; 33 | 34 | import static io.micronaut.core.util.CollectionUtils.mapOf; 35 | 36 | /** 37 | * A {@link HealthIndicator} for Grpc server. 38 | * 39 | * @author Moe Haydar 40 | * @since 2.1.0 41 | */ 42 | @Singleton 43 | @Requires(property = GrpcHealthFactory.HEALTH_ENABLED, value = StringUtils.TRUE, defaultValue = StringUtils.TRUE) 44 | @Requires(beans = HealthEndpoint.class) 45 | @Requires(beans = EmbeddedServer.class) 46 | public class GrpcServerHealthIndicator implements HealthIndicator { 47 | 48 | private static final String ID = "grpc-server"; 49 | 50 | private final EmbeddedServer server; 51 | 52 | /** 53 | * Default constructor. 54 | * 55 | * @param server The grpc embedded server 56 | */ 57 | public GrpcServerHealthIndicator(@Named("grpc.server") EmbeddedServer server) { 58 | this.server = server; 59 | } 60 | 61 | @Override 62 | public Publisher getResult() { 63 | return new AsyncSingleResultPublisher<>(this::getHealthResult); 64 | } 65 | 66 | /** 67 | * Checks if grpc is running and return status UP otherwise return status DOWN. 68 | * 69 | * @return Result with server address in the details and status UP or DOWN. 70 | */ 71 | private HealthResult getHealthResult() { 72 | final HealthStatus healthStatus = server.isRunning() ? HealthStatus.UP : HealthStatus.DOWN; 73 | final String serverHost = server.getHost(); 74 | try { 75 | 76 | int serverPort = server.getPort(); 77 | final Map details = mapOf("host", serverHost, "port", serverPort); 78 | 79 | return HealthResult 80 | .builder(ID, healthStatus) 81 | .details(details) 82 | .build(); 83 | } catch (IllegalStateException e) { 84 | /** 85 | * BUGFIX: it avoids to call the server.getPort() method when the gRPC-Server is DOWN because 86 | * it throws an unexpected exception that breaks the /health endpoint 87 | */ 88 | 89 | final Map details = mapOf("host", serverHost, "port", "N/A"); 90 | return HealthResult 91 | .builder(ID, healthStatus) 92 | .details(details) 93 | .build(); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /grpc-health/src/main/java/io/micronaut/grpc/server/health/HealthStatusManagerContainer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2022 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.micronaut.grpc.server.health; 17 | 18 | import io.grpc.protobuf.services.HealthStatusManager; 19 | import io.micronaut.context.annotation.Requires; 20 | import io.micronaut.core.util.StringUtils; 21 | 22 | import jakarta.inject.Singleton; 23 | 24 | /** 25 | * A container for the {@link HealthStatusManager}. 26 | * 27 | * @since 3.3.0 28 | */ 29 | @Singleton 30 | @Requires(classes = HealthStatusManager.class) 31 | @Requires(property = GrpcHealthFactory.HEALTH_ENABLED, value = StringUtils.TRUE, defaultValue = StringUtils.TRUE) 32 | public class HealthStatusManagerContainer { 33 | 34 | private final HealthStatusManager healthStatusManager; 35 | 36 | public HealthStatusManagerContainer(HealthStatusManager healthStatusManager) { 37 | this.healthStatusManager = healthStatusManager; 38 | } 39 | 40 | /** 41 | * @return The {@link HealthStatusManager} 42 | */ 43 | public HealthStatusManager getHealthStatusManager() { 44 | return healthStatusManager; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /grpc-health/src/test/groovy/io/micronaut/grpc/server/health/GrpcServerHealthIndicatorSpec.groovy: -------------------------------------------------------------------------------- 1 | package io.micronaut.grpc.server.health 2 | 3 | import io.micronaut.context.ApplicationContext 4 | import io.micronaut.core.io.socket.SocketUtils 5 | import io.micronaut.core.util.CollectionUtils 6 | import io.micronaut.grpc.server.GrpcEmbeddedServer 7 | import io.micronaut.health.HealthStatus 8 | import io.micronaut.management.health.indicator.HealthResult 9 | import reactor.core.publisher.Mono 10 | import spock.lang.Specification 11 | import spock.lang.Unroll 12 | 13 | class GrpcServerHealthIndicatorSpec extends Specification { 14 | void "test grpc health indicator - UP"() { 15 | def port = SocketUtils.findAvailableTcpPort() 16 | 17 | given: 18 | GrpcEmbeddedServer server = ApplicationContext.run(GrpcEmbeddedServer, ["grpc.server.port": port]) 19 | 20 | when: 21 | GrpcServerHealthIndicator healthIndicator = server.getApplicationContext().getBean(GrpcServerHealthIndicator) 22 | HealthResult result = Mono.from(healthIndicator.result).block() 23 | 24 | then: 25 | result.status == HealthStatus.UP 26 | result.details.port == port 27 | result.details.host == "localhost" 28 | 29 | cleanup: 30 | server.close() 31 | } 32 | 33 | @Unroll 34 | void "test grpc health indicator - Disabled #configvalue"() { 35 | given: 36 | GrpcEmbeddedServer server = ApplicationContext.run(GrpcEmbeddedServer, CollectionUtils.mapOf( 37 | "grpc.server.health.enabled", configvalue)) 38 | 39 | when: 40 | def optional = server.getApplicationContext().findBean(GrpcServerHealthIndicator) 41 | 42 | then: 43 | !optional.isPresent() 44 | 45 | cleanup: 46 | server.stop() 47 | 48 | where: 49 | configvalue << [false, "false", "no", ""] 50 | } 51 | 52 | void "test grpc health indicator after gRPC-Server is stopped"() { 53 | 54 | given: 55 | GrpcEmbeddedServer server = ApplicationContext.run(GrpcEmbeddedServer) 56 | 57 | when: 58 | server.stop() 59 | 60 | and: 61 | GrpcServerHealthIndicator healthIndicator = server.getApplicationContext().getBean(GrpcServerHealthIndicator) 62 | HealthResult result = Mono.from(healthIndicator.result).block() 63 | 64 | then: 65 | result.status == HealthStatus.DOWN 66 | result.details.port == "N/A" 67 | result.details.host == server.host 68 | 69 | cleanup: 70 | server.close() 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /grpc-opentracing/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'io.micronaut.build.internal.grpc-module' 3 | alias libs.plugins.protobuf 4 | } 5 | 6 | dependencies { 7 | 8 | api platform(libs.boms.grpc) 9 | api projects.micronautGrpcAnnotation 10 | api libs.managed.grpc.core 11 | api libs.managed.grpc.protobuf 12 | api libs.managed.grpc.stub 13 | 14 | // change these to "api" dependencies when this module 15 | // is removed as a dependency of "grpc-server-runtime" 16 | compileOnly libs.managed.grpc.services 17 | compileOnly libs.micronaut.tracing.legacy 18 | compileOnly mn.micronaut.management 19 | compileOnly libs.opentracing.grpc 20 | compileOnly libs.javax.annotation.api 21 | 22 | testCompileOnly libs.javax.annotation.api 23 | testCompileOnly libs.managed.protobuf.java 24 | 25 | testImplementation projects.micronautGrpcServerRuntime 26 | testImplementation libs.managed.grpc.services 27 | testImplementation libs.opentracing.mock 28 | testImplementation libs.micronaut.tracing.legacy 29 | testImplementation libs.opentracing.grpc 30 | testImplementation libs.jaeger.core 31 | testImplementation mnReactor.micronaut.reactor.http.client 32 | testImplementation mn.micronaut.management 33 | } 34 | 35 | protobuf { 36 | protoc { artifact = libs.managed.protoc.asProvider().get() } 37 | plugins { 38 | grpc { artifact = libs.managed.protoc.grpc.get() } 39 | } 40 | generateProtoTasks { 41 | all()*.plugins { grpc {} } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /grpc-opentracing/src/main/java/io/micronaut/grpc/client/tracing/GrpcClientTracingInterceptorConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.micronaut.grpc.client.tracing; 17 | 18 | import io.micronaut.context.annotation.ConfigurationBuilder; 19 | import io.micronaut.context.annotation.ConfigurationProperties; 20 | import io.micronaut.core.annotation.NonNull; 21 | import io.micronaut.core.annotation.Nullable; 22 | import io.opentracing.Tracer; 23 | import io.opentracing.contrib.grpc.ClientCloseDecorator; 24 | import io.opentracing.contrib.grpc.ClientSpanDecorator; 25 | import io.opentracing.contrib.grpc.TracingClientInterceptor; 26 | 27 | import jakarta.inject.Inject; 28 | 29 | /** 30 | * Adds a {@link TracingClientInterceptor} when OpenTracing for GRPC is on the classpath 31 | * and allows integration with Zipkin and Jaeger. 32 | * 33 | * @author graemerocher 34 | * @since 1.0 35 | */ 36 | @ConfigurationProperties(GrpcClientTracingInterceptorConfiguration.PREFIX) 37 | public class GrpcClientTracingInterceptorConfiguration { 38 | 39 | public static final String PREFIX = "grpc.client.tracing"; 40 | 41 | @ConfigurationBuilder(prefixes = "with", allowZeroArgs = true) 42 | protected final TracingClientInterceptor.Builder builder; 43 | 44 | /** 45 | * Default constructor. 46 | * 47 | * @param tracer The tracer 48 | */ 49 | protected GrpcClientTracingInterceptorConfiguration(Tracer tracer) { 50 | this.builder = TracingClientInterceptor.newBuilder().withTracer(tracer); 51 | } 52 | 53 | /** 54 | * @return The {@link TracingClientInterceptor.Builder} 55 | */ 56 | public @NonNull TracingClientInterceptor.Builder getBuilder() { 57 | return builder; 58 | } 59 | 60 | /** 61 | * Decorates the server span with custom data. 62 | * 63 | * @param clientSpanDecorator used to decorate the server span 64 | */ 65 | @Inject 66 | public void setClientSpanDecorator(@Nullable ClientSpanDecorator clientSpanDecorator) { 67 | if (clientSpanDecorator != null) { 68 | builder.withClientSpanDecorator(clientSpanDecorator); 69 | } 70 | } 71 | 72 | /** 73 | * Decorates the server span with custom data when the gRPC call is closed. 74 | * 75 | * @param clientCloseDecorator used to decorate the server span 76 | */ 77 | @Inject 78 | public void setClientCloseDecorator(@Nullable ClientCloseDecorator clientCloseDecorator) { 79 | if (clientCloseDecorator != null) { 80 | builder.withClientCloseDecorator(clientCloseDecorator); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /grpc-opentracing/src/main/java/io/micronaut/grpc/client/tracing/GrpcClientTracingInterceptorFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.micronaut.grpc.client.tracing; 17 | 18 | import io.grpc.ClientInterceptor; 19 | import io.micronaut.context.annotation.Bean; 20 | import io.micronaut.context.annotation.Factory; 21 | import io.micronaut.context.annotation.Requires; 22 | import io.micronaut.core.annotation.NonNull; 23 | 24 | import jakarta.inject.Singleton; 25 | 26 | /** 27 | * Factory that builds the Tracing interceptors. 28 | * 29 | * @author graemerocher 30 | * @since 1.0 31 | */ 32 | @Factory 33 | public class GrpcClientTracingInterceptorFactory { 34 | 35 | /** 36 | * The client interceptor. 37 | * 38 | * @param configuration The configuration 39 | * 40 | * @return The client interceptor 41 | */ 42 | @Requires(beans = GrpcClientTracingInterceptorConfiguration.class) 43 | @Singleton 44 | @Bean 45 | protected @NonNull ClientInterceptor clientTracingInterceptor(@NonNull GrpcClientTracingInterceptorConfiguration configuration) { 46 | return configuration.getBuilder().build(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /grpc-opentracing/src/main/java/io/micronaut/grpc/client/tracing/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** 17 | * Classes related to distributed tracing. 18 | */ 19 | @Configuration 20 | @Requires(classes = {Tracer.class, TracingClientInterceptor.class}) 21 | @Requires(beans = Tracer.class) 22 | package io.micronaut.grpc.client.tracing; 23 | 24 | import io.micronaut.context.annotation.Configuration; 25 | import io.micronaut.context.annotation.Requires; 26 | import io.opentracing.Tracer; 27 | import io.opentracing.contrib.grpc.TracingClientInterceptor; 28 | -------------------------------------------------------------------------------- /grpc-opentracing/src/main/java/io/micronaut/grpc/server/tracing/GrpcServerTracingInterceptorConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.micronaut.grpc.server.tracing; 17 | 18 | import io.micronaut.context.annotation.ConfigurationBuilder; 19 | import io.micronaut.context.annotation.ConfigurationProperties; 20 | import io.micronaut.core.annotation.NonNull; 21 | import io.micronaut.core.annotation.Nullable; 22 | import io.opentracing.Tracer; 23 | import io.opentracing.contrib.grpc.ServerCloseDecorator; 24 | import io.opentracing.contrib.grpc.ServerSpanDecorator; 25 | import io.opentracing.contrib.grpc.TracingServerInterceptor; 26 | 27 | import jakarta.inject.Inject; 28 | 29 | /** 30 | * Adds a {@link TracingServerInterceptor} when OpenTracing for GRPC is on the classpath 31 | * and allows integration with Zipkin and Jaeger. 32 | * 33 | * @author graemerocher 34 | * @since 1.0 35 | */ 36 | @ConfigurationProperties(GrpcServerTracingInterceptorConfiguration.PREFIX) 37 | public class GrpcServerTracingInterceptorConfiguration { 38 | 39 | public static final String PREFIX = "grpc.server.tracing"; 40 | 41 | @ConfigurationBuilder(prefixes = "with", allowZeroArgs = true) 42 | protected final TracingServerInterceptor.Builder builder; 43 | 44 | /** 45 | * Default constructor. 46 | * 47 | * @param tracer The tracer 48 | */ 49 | protected GrpcServerTracingInterceptorConfiguration(Tracer tracer) { 50 | this.builder = TracingServerInterceptor.newBuilder().withTracer(tracer); 51 | } 52 | 53 | /** 54 | * @return The {@link TracingServerInterceptor.Builder} 55 | */ 56 | public @NonNull TracingServerInterceptor.Builder getBuilder() { 57 | return builder; 58 | } 59 | 60 | /** 61 | * Decorates the server span with custom data. 62 | * 63 | * @param serverSpanDecorator used to decorate the server span 64 | */ 65 | @Inject 66 | public void setServerSpanDecorator(@Nullable ServerSpanDecorator serverSpanDecorator) { 67 | if (serverSpanDecorator != null) { 68 | builder.withServerSpanDecorator(serverSpanDecorator); 69 | } 70 | } 71 | 72 | /** 73 | * Decorates the server span with custom data when the gRPC call is closed. 74 | * 75 | * @param serverCloseDecorator used to decorate the server span 76 | */ 77 | @Inject 78 | public void setServerCloseDecorator(@Nullable ServerCloseDecorator serverCloseDecorator) { 79 | if (serverCloseDecorator != null) { 80 | builder.withServerCloseDecorator(serverCloseDecorator); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /grpc-opentracing/src/main/java/io/micronaut/grpc/server/tracing/GrpcServerTracingInterceptorFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.micronaut.grpc.server.tracing; 17 | 18 | import io.grpc.ServerInterceptor; 19 | import io.micronaut.context.annotation.Bean; 20 | import io.micronaut.context.annotation.Factory; 21 | import io.micronaut.context.annotation.Requires; 22 | import io.micronaut.core.annotation.NonNull; 23 | 24 | import jakarta.inject.Singleton; 25 | 26 | /** 27 | * Factory that builds the Tracing interceptors. 28 | * 29 | * @author graemerocher 30 | * @since 1.0 31 | */ 32 | @Factory 33 | public class GrpcServerTracingInterceptorFactory { 34 | 35 | /** 36 | * The server interceptor. 37 | * 38 | * @param configuration The configuration 39 | * 40 | * @return The server interceptor 41 | */ 42 | @Requires(beans = GrpcServerTracingInterceptorConfiguration.class) 43 | @Singleton 44 | @Bean 45 | protected @NonNull ServerInterceptor serverTracingInterceptor(@NonNull GrpcServerTracingInterceptorConfiguration configuration) { 46 | return configuration.getBuilder().build(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /grpc-opentracing/src/main/java/io/micronaut/grpc/server/tracing/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** 17 | * Classes related to distributed tracing. 18 | */ 19 | @Configuration 20 | @Requires(classes = {Tracer.class, TracingClientInterceptor.class, TracingServerInterceptor.class}) 21 | @Requires(beans = Tracer.class) 22 | package io.micronaut.grpc.server.tracing; 23 | 24 | import io.micronaut.context.annotation.Configuration; 25 | import io.micronaut.context.annotation.Requires; 26 | import io.opentracing.Tracer; 27 | import io.opentracing.contrib.grpc.TracingClientInterceptor; 28 | import io.opentracing.contrib.grpc.TracingServerInterceptor; 29 | -------------------------------------------------------------------------------- /grpc-opentracing/src/test/groovy/io/micronaut/grpc/server/tracing/GrpcTracingSpec.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.micronaut.grpc.server.tracing 17 | 18 | import io.grpc.Channel 19 | import io.grpc.Metadata 20 | import io.grpc.ServerCall 21 | import io.grpc.ServerCallHandler 22 | import io.grpc.ServerInterceptor 23 | import io.grpc.examples.helloworld.GreeterGrpc 24 | import io.grpc.examples.helloworld.HelloReply 25 | import io.grpc.examples.helloworld.HelloRequest 26 | import io.grpc.stub.StreamObserver 27 | import io.micronaut.context.annotation.Factory 28 | import io.micronaut.context.annotation.Property 29 | import io.micronaut.context.annotation.Requires 30 | import io.micronaut.grpc.annotation.GrpcChannel 31 | import io.micronaut.grpc.server.GrpcServerChannel 32 | import io.micronaut.test.annotation.MockBean 33 | import io.micronaut.test.extensions.spock.annotation.MicronautTest 34 | import io.opentracing.Tracer 35 | import io.opentracing.mock.MockTracer 36 | import jakarta.inject.Inject 37 | import jakarta.inject.Singleton 38 | import spock.lang.Specification 39 | import spock.util.concurrent.PollingConditions 40 | 41 | @MicronautTest 42 | @Property(name = 'mock.tracer', value = 'true') 43 | class GrpcTracingSpec extends Specification { 44 | 45 | @Inject 46 | TestBean testBean 47 | 48 | @Inject 49 | TracingInterceptor myInterceptor 50 | 51 | @Inject 52 | Tracer tracer 53 | 54 | MockTracer mockTracer = new MockTracer() 55 | 56 | void "test hello world grpc with tracing enabled"() { 57 | given: 58 | MockTracer tracer = mockTracer 59 | PollingConditions conditions = new PollingConditions(timeout: 3, delay: 0.5) 60 | testBean.sayHello("Fred") == "Hello Fred" 61 | 62 | expect: 63 | conditions.eventually { 64 | myInterceptor.intercepted 65 | tracer.finishedSpans().size() == 2 66 | tracer.finishedSpans()[0].operationName() == 'helloworld.Greeter/SayHello' 67 | tracer.finishedSpans()[1].operationName() == 'helloworld.Greeter/SayHello' 68 | tracer.finishedSpans().find { it.tags().get('span.kind') == 'client' } 69 | tracer.finishedSpans().find { it.tags().get('span.kind') == 'server' } 70 | } 71 | } 72 | 73 | @Singleton 74 | static class TracingInterceptor implements ServerInterceptor { 75 | 76 | boolean intercepted = false 77 | 78 | @Override 79 | ServerCall.Listener interceptCall(ServerCall call, Metadata headers, ServerCallHandler next) { 80 | intercepted = true 81 | return next.startCall(call, headers) 82 | } 83 | } 84 | 85 | @MockBean 86 | @Requires(property = "mock.tracer", value = "true") 87 | Tracer tracer() { 88 | return mockTracer 89 | } 90 | 91 | @Factory 92 | static class Clients { 93 | @Singleton 94 | GreeterGrpc.GreeterBlockingStub blockingStub(@GrpcChannel(GrpcServerChannel.NAME) Channel channel) { 95 | GreeterGrpc.newBlockingStub(channel) 96 | } 97 | } 98 | 99 | @Singleton 100 | static class TestBean { 101 | @Inject 102 | GreeterGrpc.GreeterBlockingStub blockingStub 103 | 104 | String sayHello(String message) { 105 | blockingStub.sayHello( 106 | HelloRequest.newBuilder().setName(message).build() 107 | ).message 108 | } 109 | } 110 | 111 | @Singleton 112 | static class GreeterService extends GreeterGrpc.GreeterImplBase { 113 | @Override 114 | void sayHello(HelloRequest request, StreamObserver responseObserver) { 115 | HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + request.getName()).build() 116 | responseObserver.onNext(reply) 117 | responseObserver.onCompleted() 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /grpc-opentracing/src/test/proto/helloworld.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The gRPC Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | syntax = "proto3"; 15 | 16 | option java_multiple_files = true; 17 | option java_package = "io.grpc.examples.helloworld"; 18 | option java_outer_classname = "HelloWorldProto"; 19 | option objc_class_prefix = "HLW"; 20 | 21 | package helloworld; 22 | 23 | // The greeting service definition. 24 | service Greeter { 25 | // Sends a greeting 26 | rpc SayHello (HelloRequest) returns (HelloReply) {} 27 | } 28 | 29 | // The greeting service definition. 30 | service Greeter2 { 31 | // Sends a greeting 32 | rpc SayHello (HelloRequest) returns (HelloReply) {} 33 | } 34 | 35 | // The request message containing the user's name. 36 | message HelloRequest { 37 | string name = 1; 38 | } 39 | 40 | // The response message containing the greetings 41 | message HelloReply { 42 | string message = 1; 43 | } -------------------------------------------------------------------------------- /grpc-runtime/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'io.micronaut.build.internal.grpc-module' 3 | } 4 | 5 | dependencies { 6 | api projects.micronautGrpcClientRuntime 7 | api projects.micronautGrpcServerRuntime 8 | } 9 | -------------------------------------------------------------------------------- /grpc-server-runtime/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'io.micronaut.build.internal.grpc-module' 3 | alias libs.plugins.protobuf 4 | } 5 | 6 | dependencies { 7 | 8 | api platform(libs.boms.grpc) 9 | api projects.micronautGrpcAnnotation 10 | api projects.micronautGrpcHealth 11 | api projects.micronautGrpcOpentracing 12 | api libs.managed.grpc.core 13 | api libs.managed.grpc.protobuf 14 | api libs.managed.grpc.stub 15 | 16 | compileOnly libs.managed.grpc.services 17 | compileOnly mn.micronaut.management 18 | compileOnly libs.graal.svm 19 | 20 | implementation libs.grpc.netty 21 | implementation mn.micronaut.buffer.netty 22 | implementation mn.micronaut.discovery.core 23 | 24 | testCompileOnly libs.javax.annotation.api 25 | testCompileOnly libs.managed.protobuf.java 26 | 27 | testImplementation libs.managed.grpc.services 28 | testImplementation mn.micronaut.discovery.core 29 | testImplementation mnReactor.micronaut.reactor.http.client 30 | testImplementation mn.micronaut.management 31 | testImplementation projects.micronautGrpcClientRuntime 32 | 33 | testRuntimeOnly libs.netty.tcnative.boringssl.static 34 | } 35 | 36 | sourceSets { 37 | test { 38 | java { 39 | srcDirs 'build/generated/source/proto/test/grpc' 40 | srcDirs 'build/generated/source/proto/test/java' 41 | } 42 | } 43 | } 44 | 45 | protobuf { 46 | protoc { artifact = libs.managed.protoc.asProvider().get() } 47 | plugins { 48 | grpc { artifact = libs.managed.protoc.grpc.get() } 49 | } 50 | generateProtoTasks { 51 | all()*.plugins { grpc {} } 52 | } 53 | } 54 | 55 | micronautBuild { 56 | binaryCompatibility { 57 | acceptedRegressionsFile = file("config/accepted-api-changes.json") 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /grpc-server-runtime/src/main/java/io/micronaut/grpc/server/GrpcEmbeddedServerListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.micronaut.grpc.server; 17 | 18 | import io.micronaut.context.BeanContext; 19 | import io.micronaut.context.annotation.Requires; 20 | import io.micronaut.core.annotation.Internal; 21 | import io.micronaut.runtime.event.annotation.EventListener; 22 | import io.micronaut.runtime.server.EmbeddedServer; 23 | import io.micronaut.runtime.server.event.ServerShutdownEvent; 24 | import io.micronaut.runtime.server.event.ServerStartupEvent; 25 | 26 | import jakarta.annotation.PreDestroy; 27 | import org.slf4j.Logger; 28 | import org.slf4j.LoggerFactory; 29 | 30 | import jakarta.inject.Singleton; 31 | 32 | /** 33 | * Application event listener that will start up the {@link GrpcEmbeddedServer} as a secondary server 34 | * on a different port allowing Micronaut's HTTP server and GRPC to run side by side. 35 | * 36 | * @author graemerocher 37 | * @since 1.0 38 | */ 39 | @Internal 40 | @Singleton 41 | @Requires(beans = GrpcEmbeddedServer.class) 42 | class GrpcEmbeddedServerListener implements AutoCloseable{ 43 | 44 | private static final Logger LOG = LoggerFactory.getLogger(GrpcEmbeddedServerListener.class); 45 | 46 | private final BeanContext beanContext; 47 | private GrpcEmbeddedServer grpcServer; 48 | 49 | /** 50 | * Default constructor. 51 | * 52 | * @param beanContext The bean context 53 | */ 54 | GrpcEmbeddedServerListener(BeanContext beanContext) { 55 | this.beanContext = beanContext; 56 | } 57 | 58 | @EventListener 59 | public void onServerStartupEvent(ServerStartupEvent event) { 60 | final EmbeddedServer server = event.getSource(); 61 | if (!(server instanceof GrpcEmbeddedServer)) { 62 | this.grpcServer = beanContext.getBean(GrpcEmbeddedServer.class); 63 | grpcServer.start(); 64 | if (LOG.isInfoEnabled()) { 65 | LOG.info("GRPC started on port {}", grpcServer.getPort()); 66 | } 67 | } 68 | } 69 | 70 | @EventListener 71 | public void onServerShutdownEvent(ServerShutdownEvent event) { 72 | final EmbeddedServer server = event.getSource(); 73 | if (!(server instanceof GrpcEmbeddedServer)) { 74 | this.close(); 75 | } 76 | } 77 | 78 | @Override 79 | @PreDestroy 80 | public void close() { 81 | if (grpcServer != null && grpcServer.isRunning()) { 82 | grpcServer.stop(); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /grpc-server-runtime/src/main/java/io/micronaut/grpc/server/GrpcServerChannel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.micronaut.grpc.server; 17 | 18 | import java.util.Collections; 19 | import java.util.List; 20 | import java.util.concurrent.ExecutorService; 21 | 22 | import io.grpc.ClientInterceptor; 23 | import io.grpc.ManagedChannel; 24 | import io.grpc.ManagedChannelBuilder; 25 | import io.micronaut.context.annotation.Bean; 26 | import io.micronaut.context.annotation.Factory; 27 | import io.micronaut.context.annotation.Requires; 28 | import io.micronaut.core.util.CollectionUtils; 29 | import io.micronaut.scheduling.TaskExecutors; 30 | 31 | import jakarta.inject.Named; 32 | import jakarta.inject.Singleton; 33 | 34 | /** 35 | * A factory that returns a {@link ManagedChannel} allowing communication with the embedded server. 36 | * Primarily used for testing. 37 | * 38 | * @author graemerocher 39 | * @since 1.0 40 | */ 41 | @Factory 42 | public class GrpcServerChannel { 43 | 44 | public static final String NAME = "grpc-server"; 45 | 46 | /** 47 | * Constructs a managed server channel. 48 | * 49 | * @param server The server 50 | * @param executorService The executor service 51 | * @param clientInterceptors The client interceptors 52 | * 53 | * @return The channel 54 | */ 55 | @Singleton 56 | @Named(NAME) 57 | @Requires(beans = GrpcEmbeddedServer.class) 58 | @Bean(preDestroy = "shutdown") 59 | protected ManagedChannel serverChannel( 60 | GrpcEmbeddedServer server, 61 | @Named(TaskExecutors.IO) ExecutorService executorService, 62 | List clientInterceptors) { 63 | final ManagedChannelBuilder builder = ManagedChannelBuilder.forAddress( 64 | server.getHost(), 65 | server.getPort() 66 | ).executor(executorService); 67 | if (!server.getServerConfiguration().isSecure()) { 68 | builder.usePlaintext(); 69 | } 70 | if (CollectionUtils.isNotEmpty(clientInterceptors)) { 71 | Collections.reverse(clientInterceptors); 72 | builder.intercept(clientInterceptors); 73 | } 74 | return builder.build(); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /grpc-server-runtime/src/main/java/io/micronaut/grpc/server/GrpcServerInstance.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.micronaut.grpc.server; 17 | 18 | import java.net.URI; 19 | import java.util.LinkedHashMap; 20 | import java.util.List; 21 | import java.util.Map; 22 | 23 | import io.micronaut.core.annotation.Nullable; 24 | import io.micronaut.core.convert.value.ConvertibleValues; 25 | import io.micronaut.core.util.CollectionUtils; 26 | import io.micronaut.core.util.StringUtils; 27 | import io.micronaut.discovery.EmbeddedServerInstance; 28 | import io.micronaut.discovery.metadata.ServiceInstanceMetadataContributor; 29 | import io.micronaut.runtime.server.EmbeddedServer; 30 | 31 | /** 32 | * Implementation of the {@link EmbeddedServerInstance} interface for GRPC. 33 | * 34 | * @author graemerocher 35 | * @author Iván López 36 | * @since 1.0 37 | */ 38 | class GrpcServerInstance implements EmbeddedServerInstance { 39 | 40 | private final String id; 41 | private final URI uri; 42 | private final ConvertibleValues metadata; 43 | private final EmbeddedServer embeddedServer; 44 | 45 | /** 46 | * Default constructor. 47 | * 48 | * @param embeddedServer The embedded server 49 | * @param id The ID 50 | * @param uri The URI 51 | * @param metadata The metadata 52 | * @param metadataContributors The metadata contributors 53 | * @param grpcConfiguration The GRPC Configuration 54 | */ 55 | GrpcServerInstance( 56 | EmbeddedServer embeddedServer, 57 | String id, 58 | URI uri, 59 | @Nullable Map metadata, 60 | @Nullable List metadataContributors, 61 | GrpcServerConfiguration grpcConfiguration) { 62 | this.embeddedServer = embeddedServer; 63 | this.id = calculateInstanceId(id, grpcConfiguration); 64 | this.uri = uri; 65 | if (metadata == null) { 66 | metadata = new LinkedHashMap<>(5); 67 | } 68 | 69 | if (CollectionUtils.isNotEmpty(metadataContributors)) { 70 | for (ServiceInstanceMetadataContributor contributor : metadataContributors) { 71 | contributor.contribute(this, metadata); 72 | } 73 | } 74 | 75 | this.metadata = ConvertibleValues.of(metadata); 76 | } 77 | 78 | @Override 79 | public String getId() { 80 | return id; 81 | } 82 | 83 | @Override 84 | public URI getURI() { 85 | return uri; 86 | } 87 | 88 | @Override 89 | public ConvertibleValues getMetadata() { 90 | return metadata; 91 | } 92 | 93 | @Override 94 | public EmbeddedServer getEmbeddedServer() { 95 | return embeddedServer; 96 | } 97 | 98 | private String calculateInstanceId(String id, @Nullable GrpcServerConfiguration grpcConfiguration) { 99 | if (grpcConfiguration != null && StringUtils.isNotEmpty(grpcConfiguration.getInstanceId())) { 100 | return grpcConfiguration.getInstanceId(); 101 | } else { 102 | return id; 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /grpc-server-runtime/src/main/java/io/micronaut/grpc/server/GrpcSslConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.micronaut.grpc.server; 17 | 18 | import java.util.Optional; 19 | 20 | import io.micronaut.context.annotation.ConfigurationProperties; 21 | import io.micronaut.core.annotation.Nullable; 22 | 23 | /** 24 | * Configuration for the SSL properties of GRPC. 25 | * 26 | * @author graemerocher 27 | * @since 1.0 28 | */ 29 | @ConfigurationProperties(GrpcServerConfiguration.PREFIX + ".ssl") 30 | public class GrpcSslConfiguration { 31 | 32 | private String certChain; 33 | 34 | private String privateKey; 35 | 36 | /** 37 | * @return The cert chain 38 | */ 39 | public Optional getCertChain() { 40 | return Optional.ofNullable(certChain); 41 | } 42 | 43 | /** 44 | * @param certChain Sets the cert chain 45 | */ 46 | public void setCertChain(@Nullable String certChain) { 47 | this.certChain = certChain; 48 | } 49 | 50 | /** 51 | * @return The private key 52 | */ 53 | public Optional getPrivateKey() { 54 | return Optional.ofNullable(privateKey); 55 | } 56 | 57 | /** 58 | * @param privateKey Sets the private key 59 | */ 60 | public void setPrivateKey(@Nullable String privateKey) { 61 | this.privateKey = privateKey; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /grpc-server-runtime/src/main/java/io/micronaut/grpc/server/interceptor/OrderedServerInterceptor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2020 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.micronaut.grpc.server.interceptor; 17 | 18 | import io.grpc.Metadata; 19 | import io.grpc.ServerCall; 20 | import io.grpc.ServerCallHandler; 21 | import io.grpc.ServerInterceptor; 22 | import io.micronaut.core.order.Ordered; 23 | 24 | /** 25 | * A {@link ServerInterceptor} implementation which allows ordering and simply passes all 26 | * calls to a delegate interceptor. 27 | * 28 | * @author brianwyka 29 | * @since 2.0.2 30 | */ 31 | public class OrderedServerInterceptor implements ServerInterceptor, Ordered { 32 | 33 | private final ServerInterceptor delegate; 34 | private final int order; 35 | 36 | /** 37 | * Constructs an instance of this interceptor with the provided delegate interceptor and order. 38 | * 39 | * @param delegate the interceptor to delegate to 40 | * @param order the order number 41 | */ 42 | public OrderedServerInterceptor(final ServerInterceptor delegate, final int order) { 43 | this.delegate = delegate; 44 | this.order = order; 45 | } 46 | 47 | /** 48 | * Delegates interceptor logic to {@link #delegate} interceptor. 49 | * 50 | * {@inheritDoc} 51 | */ 52 | @Override 53 | public ServerCall.Listener interceptCall(final ServerCall call, final Metadata headers, final ServerCallHandler next) { 54 | return delegate.interceptCall(call, headers, next); 55 | } 56 | 57 | /** 58 | * Get the order in which this interceptor should execute in the interceptor chain. 59 | * 60 | * @return the order 61 | */ 62 | @Override 63 | public int getOrder() { 64 | return order; 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /grpc-server-runtime/src/main/java/io/micronaut/grpc/server/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** 17 | * Classes related to the GRPC server. 18 | */ 19 | package io.micronaut.grpc.server; 20 | -------------------------------------------------------------------------------- /grpc-server-runtime/src/test/groovy/io/micronaut/grpc/GrpcNamedChannelSpec.groovy: -------------------------------------------------------------------------------- 1 | package io.micronaut.grpc 2 | 3 | import io.grpc.Channel 4 | import io.grpc.examples.helloworld.Greeter2Grpc 5 | import io.grpc.examples.helloworld.HelloReply 6 | import io.grpc.examples.helloworld.HelloRequest 7 | import io.grpc.stub.StreamObserver 8 | import io.micronaut.context.ApplicationContext 9 | import io.micronaut.context.annotation.Factory 10 | import io.micronaut.core.io.socket.SocketUtils 11 | import io.micronaut.grpc.annotation.GrpcChannel 12 | import io.micronaut.grpc.channels.GrpcManagedChannelConfiguration 13 | import io.micronaut.inject.qualifiers.Qualifiers 14 | import io.micronaut.runtime.server.EmbeddedServer 15 | import jakarta.inject.Inject 16 | import jakarta.inject.Singleton 17 | import spock.lang.Retry 18 | import spock.lang.Specification 19 | 20 | class GrpcNamedChannelSpec extends Specification { 21 | 22 | // retry because on Cloud CI you may have a race condition regarding port availability and binding 23 | @Retry 24 | void "test named client"() { 25 | given: 26 | def port = SocketUtils.findAvailableTcpPort() 27 | EmbeddedServer embeddedServer = ApplicationContext.run(EmbeddedServer, [ 28 | 'grpc.server.port' : port, 29 | 'grpc.channels.greeter.address' : "localhost:$port", 30 | 'grpc.channels.greeter.plaintext': true 31 | ]) 32 | def context = embeddedServer.getApplicationContext() 33 | def testBean = context.getBean(TestBean) 34 | def config = context.getBean(GrpcManagedChannelConfiguration, Qualifiers.byName("greeter")) 35 | def channel = testBean.blockingStub.channel 36 | 37 | expect: 38 | channel != null 39 | 40 | testBean.sayHello("Fred") == "Hello 2 Fred" 41 | config.name == 'greeter' 42 | 43 | cleanup: 44 | embeddedServer.close() 45 | } 46 | 47 | @Singleton 48 | static class TestBean { 49 | @Inject 50 | Greeter2Grpc.Greeter2BlockingStub blockingStub 51 | 52 | String sayHello(String message) { 53 | blockingStub.sayHello( 54 | HelloRequest.newBuilder().setName(message).build() 55 | ).message 56 | } 57 | } 58 | 59 | 60 | @Factory 61 | static class Clients { 62 | @Singleton 63 | Greeter2Grpc.Greeter2BlockingStub blockingStub(@GrpcChannel("greeter") Channel channel) { 64 | Greeter2Grpc.newBlockingStub(channel) 65 | } 66 | } 67 | 68 | @Singleton 69 | static class GreeterImpl extends Greeter2Grpc.Greeter2ImplBase { 70 | @Override 71 | void sayHello(HelloRequest request, StreamObserver responseObserver) { 72 | HelloReply reply = HelloReply.newBuilder().setMessage("Hello 2 " + request.getName()).build() 73 | responseObserver.onNext(reply) 74 | responseObserver.onCompleted() 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /grpc-server-runtime/src/test/groovy/io/micronaut/grpc/GrpcServerConfigurationSpec.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.micronaut.grpc 17 | 18 | import io.grpc.ServerBuilder 19 | import io.micronaut.context.ApplicationContext 20 | import io.micronaut.core.io.socket.SocketUtils 21 | import io.micronaut.grpc.server.GrpcServerConfiguration 22 | import io.micronaut.test.extensions.spock.annotation.MicronautTest 23 | import spock.lang.Specification 24 | 25 | @MicronautTest 26 | class GrpcServerConfigurationSpec extends Specification { 27 | 28 | void "test GRPC configuration"() { 29 | given: 30 | def port = SocketUtils.findAvailableTcpPort() 31 | def ctx = ApplicationContext.run([ 32 | 'grpc.server.port' : port, 33 | 'grpc.server.handshake-timeout': '11s', 34 | 'grpc.server.instance-id' : 'hello-grpc' 35 | ]) 36 | 37 | GrpcServerConfiguration configuration = ctx.getBean(GrpcServerConfiguration) 38 | ServerBuilder serverBuilder = configuration.getServerBuilder() 39 | def server = serverBuilder.build() 40 | server.start() 41 | 42 | expect: 43 | serverBuilder != null 44 | server.getPort() == port 45 | configuration.instanceId == 'hello-grpc' 46 | 47 | cleanup: 48 | server.shutdown().awaitTermination() 49 | ctx.close() 50 | } 51 | 52 | void "test GRPC SSL configuration"() { 53 | given: 54 | def port = SocketUtils.findAvailableTcpPort() 55 | def ctx = ApplicationContext.run([ 56 | 'grpc.server.port' : port, 57 | 'grpc.server.ssl.cert-chain' : 'classpath:example.crt', 58 | 'grpc.server.ssl.private-key': 'classpath:example.key', 59 | ]) 60 | 61 | when: 62 | GrpcServerConfiguration configuration = ctx.getBean(GrpcServerConfiguration) 63 | ServerBuilder serverBuilder = configuration.getServerBuilder() 64 | def server = serverBuilder.build() 65 | server.start() 66 | 67 | then: 68 | noExceptionThrown() 69 | 70 | cleanup: 71 | server.shutdown().awaitTermination() 72 | ctx.close() 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /grpc-server-runtime/src/test/groovy/io/micronaut/grpc/HelloWordGrpcSpec.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.micronaut.grpc 17 | 18 | 19 | import io.grpc.Channel 20 | import io.grpc.Metadata 21 | import io.grpc.ServerCall 22 | import io.grpc.ServerCallHandler 23 | import io.grpc.ServerInterceptor 24 | import io.grpc.examples.helloworld.GreeterGrpc 25 | import io.grpc.examples.helloworld.HelloReply 26 | import io.grpc.examples.helloworld.HelloRequest 27 | import io.grpc.stub.StreamObserver 28 | import io.micronaut.context.annotation.Factory 29 | import io.micronaut.grpc.annotation.GrpcChannel 30 | import io.micronaut.grpc.server.GrpcServerChannel 31 | import io.micronaut.test.extensions.spock.annotation.MicronautTest 32 | import jakarta.inject.Inject 33 | import jakarta.inject.Singleton 34 | import spock.lang.Specification 35 | 36 | @MicronautTest 37 | class HelloWordGrpcSpec extends Specification { 38 | 39 | 40 | @Inject 41 | TestBean testBean 42 | 43 | @Inject 44 | MyInterceptor myInterceptor 45 | 46 | void "test hello world grpc"() { 47 | expect: 48 | testBean.sayHello("Fred") == "Hello Fred" 49 | myInterceptor.intercepted 50 | } 51 | 52 | 53 | @Factory 54 | static class Clients { 55 | @Singleton 56 | GreeterGrpc.GreeterBlockingStub blockingStub(@GrpcChannel(GrpcServerChannel.NAME) Channel channel) { 57 | GreeterGrpc.newBlockingStub(channel) 58 | } 59 | } 60 | 61 | @Singleton 62 | static class MyInterceptor implements ServerInterceptor { 63 | 64 | boolean intercepted = false 65 | 66 | @Override 67 | ServerCall.Listener interceptCall(ServerCall call, Metadata headers, ServerCallHandler next) { 68 | intercepted = true 69 | return next.startCall(call, headers) 70 | } 71 | } 72 | 73 | @Singleton 74 | static class TestBean { 75 | @Inject 76 | GreeterGrpc.GreeterBlockingStub blockingStub 77 | 78 | String sayHello(String message) { 79 | blockingStub.sayHello( 80 | HelloRequest.newBuilder().setName(message).build() 81 | ).message 82 | } 83 | } 84 | 85 | @Singleton 86 | static class GreeterImpl extends GreeterGrpc.GreeterImplBase { 87 | @Override 88 | void sayHello(HelloRequest request, StreamObserver responseObserver) { 89 | HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + request.getName()).build() 90 | responseObserver.onNext(reply) 91 | responseObserver.onCompleted() 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /grpc-server-runtime/src/test/groovy/io/micronaut/grpc/ManagedChannelBuilderListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.micronaut.grpc; 17 | 18 | import io.grpc.ManagedChannelBuilder; 19 | import io.micronaut.context.event.BeanCreatedEvent; 20 | import io.micronaut.context.event.BeanCreatedEventListener; 21 | 22 | import jakarta.inject.Singleton; 23 | 24 | @Singleton 25 | public class ManagedChannelBuilderListener implements BeanCreatedEventListener> { 26 | 27 | @Override 28 | public ManagedChannelBuilder onCreated(BeanCreatedEvent> event) { 29 | final ManagedChannelBuilder channelBuilder = event.getBean(); 30 | channelBuilder.maxInboundMessageSize(1024); 31 | return channelBuilder; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /grpc-server-runtime/src/test/groovy/io/micronaut/grpc/ServerBuilderListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.micronaut.grpc; 17 | 18 | import io.grpc.ServerBuilder; 19 | import io.micronaut.context.event.BeanCreatedEvent; 20 | import io.micronaut.context.event.BeanCreatedEventListener; 21 | 22 | import jakarta.inject.Singleton; 23 | 24 | @Singleton 25 | public class ServerBuilderListener implements BeanCreatedEventListener> { 26 | 27 | @Override 28 | public ServerBuilder onCreated(BeanCreatedEvent> event) { 29 | final ServerBuilder builder = event.getBean(); 30 | builder.maxInboundMessageSize(1024); 31 | return builder; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /grpc-server-runtime/src/test/groovy/io/micronaut/grpc/server/interceptor/OrderedServerInterceptorSpec.groovy: -------------------------------------------------------------------------------- 1 | package io.micronaut.grpc.server.interceptor 2 | 3 | import io.grpc.Metadata 4 | import io.grpc.ServerCall 5 | import io.grpc.ServerCallHandler 6 | import io.grpc.ServerInterceptor 7 | import spock.lang.Specification 8 | 9 | class OrderedServerInterceptorSpec extends Specification { 10 | 11 | def "test interceptCall"() { 12 | given: 13 | ServerInterceptor mockDelegate = Mock() 14 | OrderedServerInterceptor orderedServerInterceptor = new OrderedServerInterceptor(mockDelegate, 1) 15 | 16 | ServerCall mockServerCall = Mock() 17 | Metadata metadata = new Metadata() 18 | ServerCallHandler mockServerCallHandler = Mock() 19 | 20 | ServerCall.Listener returnedListener = new ServerCall.Listener() {} 21 | 22 | when: 23 | ServerCall.Listener listener = orderedServerInterceptor.interceptCall(mockServerCall, metadata, mockServerCallHandler) 24 | 25 | then: 26 | 1 * mockDelegate.interceptCall(mockServerCall, metadata, mockServerCallHandler) >> returnedListener 27 | 0 * _ 28 | 29 | and: 30 | listener == returnedListener 31 | } 32 | 33 | def "test getOrder"() { 34 | given: 35 | int order = 10 36 | OrderedServerInterceptor orderedServerInterceptor = new OrderedServerInterceptor(Mock(ServerInterceptor), 10) 37 | 38 | expect: 39 | orderedServerInterceptor.order == order 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /grpc-server-runtime/src/test/proto/helloworld.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The gRPC Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | syntax = "proto3"; 15 | 16 | option java_multiple_files = true; 17 | option java_package = "io.grpc.examples.helloworld"; 18 | option java_outer_classname = "HelloWorldProto"; 19 | option objc_class_prefix = "HLW"; 20 | 21 | package helloworld; 22 | 23 | // The greeting service definition. 24 | service Greeter { 25 | // Sends a greeting 26 | rpc SayHello (HelloRequest) returns (HelloReply) {} 27 | } 28 | 29 | // The greeting service definition. 30 | service Greeter2 { 31 | // Sends a greeting 32 | rpc SayHello (HelloRequest) returns (HelloReply) {} 33 | } 34 | 35 | // The request message containing the user's name. 36 | message HelloRequest { 37 | string name = 1; 38 | } 39 | 40 | // The response message containing the greetings 41 | message HelloReply { 42 | string message = 1; 43 | } -------------------------------------------------------------------------------- /grpc-server-runtime/src/test/resources/example.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFazCCA1OgAwIBAgIUeNH4sv/Qq1xJ9AhhsjAfN8x2E6EwDQYJKoZIhvcNAQEL 3 | BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM 4 | GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDA4MjEwODUzMjdaFw0zMDA4 5 | MTkwODUzMjdaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw 6 | HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggIiMA0GCSqGSIb3DQEB 7 | AQUAA4ICDwAwggIKAoICAQCuCTdpW29qrE2VR+HYClgMGWzMSi10aAbMBVnk03Wh 8 | sxWKamAD7JKTi+GTZnUaSCQDd2z/BanK0B2dz70b9vIpdIOivOEZdkeoZ0/Frhma 9 | elAnrFUU8u1af+V6jD3m5PIMwwClcPRY1hbs2ojjuOSQ0HikksLjbqrorm9eFFu9 10 | LdfgpZi3A40ud+75zACOkS3yyBMVjI1LXsklbPBPVVU1PVQhENCM1a36OBqiC/3Q 11 | Y5jqG6K2equiqDw6rHSANWc4GnE6vQHXU2HYzC+YDC3VgJ4xArZbKcihqTWHHrqx 12 | amaYGgCybWaimj+aUDH8Uhr5iieZx2TGRh6rwuekTzxNF6KEeJcmQd1nFFG4er5x 13 | Zltdyf50galo+qYePkFS6RSfIBeDXU8X1e1HxPSd+TyKsHtuHzxOIAkUrQu9ABAS 14 | 7CsOP+34rWmafnHsx/IDS+O6KSPP9c2So6esMyR+yaHLjDUM3dL3u+yKq/48g4LL 15 | Lx0yOtaqtJfpeeaWjp88mFlA33XQURJnTC2xp2pr6m9ey94MXPEIw+CqpZ9ficbz 16 | KAbeBqW/iUTSnyLtxc2qlLKV6kszJxQ10p9gnd40mz+iloosIScehZOVuGmGeNdw 17 | C/xiiCxrYMl+7gDLPIADnTkAMtyhKsnmfalf1ic35l5wl06ZuU4SOKPiZPBlDlXc 18 | yQIDAQABo1MwUTAdBgNVHQ4EFgQU5/rVJIPrMRDEayzRPQ3A24Q7hikwHwYDVR0j 19 | BBgwFoAU5/rVJIPrMRDEayzRPQ3A24Q7hikwDwYDVR0TAQH/BAUwAwEB/zANBgkq 20 | hkiG9w0BAQsFAAOCAgEAQcYjKCNf6mnMZ4CZ6lWv00B2FcJj2p57tQ6DY+nEZU8P 21 | fTiCFdC2Kjl/st2y5T6JapWd//L+RNaFPGw4AHL4193tTAX5klgGjOhL28Wd7LVF 22 | 50mELYzbXGXAcGPyMqxIyc1ByRP4/rPoa0gjLz+/vnxuiNHzHPX0JxmrBK1P6yCG 23 | jOGvt30BOMzljrsLKGAK9NRZ/OweWHim8p8osTH00FbAbY85YqA0I/oKO/ta1Hiw 24 | hK+RCN/1xixoW4JZ5D1bzMdpW0BluaivLrHV5OuBlzYjHq5ACMXoRyqI5Lqhbptp 25 | OxxMnxE8ESzFEqGZ4gdvoGsMu2ibpmfJZKMGGIKoE/EgglltPRcmDwUe48Xmn0SB 26 | BV4AN49+BaDi0rczC1kt4XUKfBkPOZuVyfds6dUJuKdziy0yrnPGR87IhL269KTS 27 | JDKgmtYpvVZ7aptShWCQ3Wvq6B45FBCQmCdBkcnlbYDA7aRtnCawRWgqNmj3sDWF 28 | DM+3qEXZxKVQhWw+ccKBJLnA9gs+pfCgYxyH7IAPEN28NVY5MOOBW8jsIT0xPE84 29 | Operh1jv9wXV3Bgmgm0KJe9C3CKYvqrELMXkSFDnnIyAnE22zOys3vUINSHAth71 30 | TZQPAVxZ9Evf0ogZ1yMauXaafKWcLxvkAUVBCNmwlmVJH28BCvcTJEkVRWz3f8w= 31 | -----END CERTIFICATE----- 32 | -------------------------------------------------------------------------------- /grpc-server-runtime/src/test/resources/example.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQCuCTdpW29qrE2V 3 | R+HYClgMGWzMSi10aAbMBVnk03WhsxWKamAD7JKTi+GTZnUaSCQDd2z/BanK0B2d 4 | z70b9vIpdIOivOEZdkeoZ0/FrhmaelAnrFUU8u1af+V6jD3m5PIMwwClcPRY1hbs 5 | 2ojjuOSQ0HikksLjbqrorm9eFFu9LdfgpZi3A40ud+75zACOkS3yyBMVjI1LXskl 6 | bPBPVVU1PVQhENCM1a36OBqiC/3QY5jqG6K2equiqDw6rHSANWc4GnE6vQHXU2HY 7 | zC+YDC3VgJ4xArZbKcihqTWHHrqxamaYGgCybWaimj+aUDH8Uhr5iieZx2TGRh6r 8 | wuekTzxNF6KEeJcmQd1nFFG4er5xZltdyf50galo+qYePkFS6RSfIBeDXU8X1e1H 9 | xPSd+TyKsHtuHzxOIAkUrQu9ABAS7CsOP+34rWmafnHsx/IDS+O6KSPP9c2So6es 10 | MyR+yaHLjDUM3dL3u+yKq/48g4LLLx0yOtaqtJfpeeaWjp88mFlA33XQURJnTC2x 11 | p2pr6m9ey94MXPEIw+CqpZ9ficbzKAbeBqW/iUTSnyLtxc2qlLKV6kszJxQ10p9g 12 | nd40mz+iloosIScehZOVuGmGeNdwC/xiiCxrYMl+7gDLPIADnTkAMtyhKsnmfalf 13 | 1ic35l5wl06ZuU4SOKPiZPBlDlXcyQIDAQABAoICACCt1lvQMYGkTQUk9EFu382V 14 | 0Jojq5laFzykHJcdJc9xIzBUfSb/ex59e6QD7yU6Opj0CeFxHMrafVUutuHTYvFp 15 | 0XXzZYk0bowuqgoCgQhCw15Pu8ItQ2hk76AtSUpb3x+KVkq6hQdRXAipmF66TyDq 16 | JF0yuamfFDSQ3JSb2gYR36FtNtnWruH030jEh649kJUwAHVhaP6oE7kVVaJv8YoH 17 | tA5pa9+mF/8OHeIjwkspCIOdINaG/keSs4yI6W9Rl8ovHOfcqcXXAF6HKMCyOMEI 18 | X6vwSHGhzOyeK1Dt8jxyMEWLTGo0pLsPn7XILTql6C7HRFxZ1pBshkHWXUy7HCVG 19 | Y+kwBiO75D86EqBZwOr9pkcjdcc9YDtntA/Ug5TLkw3umMXXTgV/eS+UiVakcdz9 20 | h8b1KhKzKyiUmaHy27t/3W6UKOhAXULBIeV+fy5FabTtz7QE+SP1/6VJ+zwp3nHy 21 | CBwh15q1taiKbyLWdbkrcmSYRVPmxtS8EbLEG5TowEtlGVdYjzGS1iRCYwHzJHcM 22 | QVdO5xLseTv1/lXmkiLV7Qyf+HE3mEBvQnyN8DajBY1s6y79WUKY66+PEYr2Mmhg 23 | OLbrlaPYsRn8F44rVBJrkMxSLc5pdVjjpgcQyQRlV2gAL/EdLkkdCC8AFezfev6h 24 | FDlyR8wmQkpYrBQbys5pAoIBAQDacRC6UDKcP1F+z+HnCIteH/s1t7NF3+f3/+Ia 25 | 9BMOmzN/uiD7xVSxv7eR7xh9+9R3tQdNaNJa9FGS3BOnWd2bXq32NukElOc9AhsP 26 | mjoqHYEQBmNFJs43wmQgvTRWmldtbaUrG1Rt5UEMYdjfg3oL32QmVtGFpcvefN2X 27 | WN+X4oAXXlQ/nAL2hs0D64u6tAy+RryHvhPSesjXRG1AXnc0Tg7QnfTLpg83VePU 28 | +JvOgpTxXAR8w28Y0WQUiU3UVfoC3kIi0gi+OoxHt595alwtPhIFJhV0IW7vZ7Qq 29 | u6GQ36mtRZtfUWpgisCerXcNvc6P5Dt2mDWkqbOqekfE99KXAoIBAQDL9ZccB2Ic 30 | Bx2QzwyyuNZLG4pRcENE3j+7eRo3nhqM90m5mtOr+T+4J8IXIGpxNxzVh4Z2RacD 31 | k2pHcuKLT5nikL7zD6GV/l2qGm5mKFbUI3RQJWqYfvkHbQ5KD5/ib8NT3d5mdaPG 32 | 2ASuTe+cVIh9k5hacLNHRRZuot8UrtBovo+9t3MKtgvBYAQKPEIKPICYS+PHFjX7 33 | a/KMMizOoP9CJq29JMQ1NMBWOWJNbjzuSp9Fuc3WdDm4YletzASdHLJh+Lxt0vRH 34 | eURpjF5Yn6znSfIWjlUbjRhA21ppgwmWRquye9O4QuP5z5ysU6oEW5tUM6/2tmTA 35 | nG6oBW4LxZefAoIBAF9u9jfvwayxnREiETe2a+z7W+zkDNaCM/4NNONBrvG6rI0o 36 | 7DASziW59KE1LCYeV3zVAhO3r+88vkbDD3MVt9OhUa833iW6SCxHNzthzfeIXFJ7 37 | 9/LGOWHy83u6LZuIYpnURUzH6+L3PawDM5SzBCcbuc1mMLOK876IHud4VHcu5XdQ 38 | Js6DgmfiDL+hsLIo50hZ7xP+3vod2pDxvClbHAkl8SMDX2d5bTxnsdnho3BdbGTm 39 | Jq+7UlYtZOPz+KTDyy5lm89Ko7c4LQFIH8wTw6GgdI77THQoBydgzz28K6H3lVG2 40 | D2NdVnRHKlAoyNKgEhabR79QUF9YJL5eHezXuJECggEAZYIYL8n46SwdadmyXyyg 41 | 8oaFY5y0zgyyuXI/OxkD686TMb29xWRqcxPOhEYM08XzIs6tfCWURrJSfbbOyzjV 42 | xK33au8Ho6gSI7u94DgJZtEybUUB9V2UQT7kkrWOBFtNYmsU6fd4iFkwkzOUokBs 43 | IduYQsK+ZyUaUfvbfOa4MLlOvsQTjGwoE5jeyd1NnNyZv7JdbdM/EJ+b+mxqYng8 44 | MkcfxvgKctSLOR6cLx9DUfFvrXsY17fGll7TdlsInM0QdrQdy6bnDr9q7gD/X6ow 45 | mnfx9YnnD6w8OuMw7zxZRCJuB4mbSKR54/WQ6y9EeaDCALksxWPnZvk0FYmGSHOR 46 | zwKCAQEAtUsT91bOzB/wSo3T7qethdNn7r02VneBB4j93m4UJHHxyIMHROx1iBoK 47 | 9Ptne0cAI3lQzwZ/FKIj/0qPcHFhemuA5RMe6hB3JTfNoyxVYHwB6FU6RyE4cW8y 48 | rcsXAAiXNB3Z7aA3AX2zkAb4TRea+tBiyHLlIR6+dBFDHVf1e7auVREIg18hH3DJ 49 | zQ/g3yhJlucz9FbBDV77oHv1jpPH0ONNXoMGg/qhEV6iE2rRbSsinO2ZQBpzoOjQ 50 | fx69d+Xu8rXEgFlCnsXuiQ9q7Bct4tvv4QMLN8Jn0CNC+/UB+r9Oihh51/QKW3sL 51 | 7aK5AkoUiB014BYf67L01eOs0o0Xmg== 52 | -----END PRIVATE KEY----- 53 | -------------------------------------------------------------------------------- /grpc-server-runtime/src/test/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /protobuff-support/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'io.micronaut.build.internal.grpc-module' 3 | alias libs.plugins.protobuf 4 | } 5 | 6 | dependencies { 7 | api platform(libs.boms.protobuf) 8 | api mn.micronaut.http 9 | api mn.micronaut.http.netty 10 | //protobuf 11 | api libs.managed.protobuf.java 12 | api libs.managed.protobuf.java.util 13 | 14 | testImplementation mn.micronaut.http.server.netty 15 | testImplementation mnReactor.micronaut.reactor.http.client 16 | testImplementation mn.micronaut.jackson.databind 17 | testImplementation libs.jackson.datatype.protobuf 18 | 19 | testRuntimeOnly mn.micronaut.jackson.databind 20 | } 21 | 22 | protobuf { 23 | protoc { artifact = libs.managed.protoc.asProvider().get() } 24 | } 25 | -------------------------------------------------------------------------------- /protobuff-support/src/main/java/io/micronaut/protobuf/codec/ExtensionRegistryFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.micronaut.protobuf.codec; 17 | 18 | import com.google.protobuf.ExtensionRegistry; 19 | import io.micronaut.context.annotation.Factory; 20 | import io.micronaut.context.annotation.Requires; 21 | 22 | import jakarta.inject.Singleton; 23 | 24 | /** 25 | * Creates the default {@link ExtensionRegistry}. 26 | * 27 | * @author graemerocher 28 | * @since 1.0 29 | */ 30 | @Factory 31 | @Requires(classes = ExtensionRegistry.class) 32 | public class ExtensionRegistryFactory { 33 | 34 | /** 35 | * Constructs the extension registry. 36 | * @return The extension registry 37 | */ 38 | @Singleton 39 | protected ExtensionRegistry extensionRegistry() { 40 | return ExtensionRegistry.newInstance(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /protobuff-support/src/main/java/io/micronaut/protobuf/convert/ByteBufToProtoMessageConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.micronaut.protobuf.convert; 17 | 18 | import java.io.IOException; 19 | import java.util.Optional; 20 | 21 | import jakarta.inject.Singleton; 22 | 23 | import com.google.protobuf.Message; 24 | 25 | import io.micronaut.context.annotation.Requires; 26 | import io.micronaut.core.convert.ConversionContext; 27 | import io.micronaut.core.convert.TypeConverter; 28 | import io.micronaut.protobuf.codec.ProtobufferCodec; 29 | import io.netty.buffer.ByteBuf; 30 | import io.netty.buffer.ByteBufInputStream; 31 | 32 | /** 33 | * Converts Protocol buffer messages from Netty {@link ByteBuf}. 34 | * 35 | * @author graemerocher 36 | * @author luistrigueiros 37 | */ 38 | @Singleton 39 | @Requires(classes = {Message.class, ByteBuf.class}) 40 | public class ByteBufToProtoMessageConverter implements TypeConverter { 41 | 42 | private final ProtobufferCodec codec; 43 | 44 | /** 45 | * Default constructor. 46 | * 47 | * @param codec The codec 48 | */ 49 | public ByteBufToProtoMessageConverter(ProtobufferCodec codec) { 50 | this.codec = codec; 51 | } 52 | 53 | @Override 54 | public Optional convert(ByteBuf object, Class targetType, ConversionContext context) { 55 | return codec 56 | .getMessageBuilder(targetType) 57 | .flatMap(builder -> rehydrate(object, builder, context)); 58 | } 59 | 60 | private Optional rehydrate(ByteBuf object, Message.Builder builder, ConversionContext context) { 61 | try (ByteBufInputStream byteBufInputStream = new ByteBufInputStream(object.copy(), true)) { 62 | builder.mergeFrom(byteBufInputStream, codec.getExtensionRegistry()); 63 | return Optional.of(builder.build()); 64 | } catch (IOException e) { 65 | context.reject(e); 66 | return Optional.empty(); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /protobuff-support/src/main/java/io/micronaut/protobuf/convert/ProtoMessageToByteBufConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.micronaut.protobuf.convert; 17 | 18 | import com.google.protobuf.Message; 19 | import io.micronaut.context.annotation.Requires; 20 | import io.micronaut.core.convert.ConversionContext; 21 | import io.micronaut.core.convert.ConversionService; 22 | import io.micronaut.core.convert.TypeConverter; 23 | import io.netty.buffer.ByteBuf; 24 | 25 | import jakarta.inject.Singleton; 26 | import java.util.Optional; 27 | 28 | /** 29 | * Converts Protocol buffer messages to Netty {@link ByteBuf}. 30 | * 31 | * @author graemerocher 32 | * @author luistrigueiros 33 | */ 34 | @Singleton 35 | @Requires(classes = {Message.class, ByteBuf.class}) 36 | public class ProtoMessageToByteBufConverter implements TypeConverter { 37 | private final ConversionService conversionService; 38 | 39 | /** 40 | * @param conversionService The conversion service 41 | */ 42 | public ProtoMessageToByteBufConverter(ConversionService conversionService) { 43 | this.conversionService = conversionService; 44 | } 45 | 46 | @Override 47 | public Optional convert(Message object, Class targetType, ConversionContext context) { 48 | return conversionService.convert(object.toByteArray(), targetType, context); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /protobuff-support/src/main/java/io/micronaut/protobuf/handler/ProtobufBodyHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2023 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.micronaut.protobuf.handler; 17 | 18 | import com.google.protobuf.ExtensionRegistry; 19 | import com.google.protobuf.Message; 20 | import io.micronaut.core.type.Argument; 21 | import io.micronaut.core.type.Headers; 22 | import io.micronaut.core.type.MutableHeaders; 23 | import io.micronaut.http.HttpHeaders; 24 | import io.micronaut.http.MediaType; 25 | import io.micronaut.http.annotation.Consumes; 26 | import io.micronaut.http.annotation.Produces; 27 | import io.micronaut.http.body.MessageBodyHandler; 28 | import io.micronaut.http.codec.CodecException; 29 | import io.micronaut.protobuf.codec.ProtobufferCodec; 30 | import jakarta.inject.Singleton; 31 | 32 | import java.io.IOException; 33 | import java.io.InputStream; 34 | import java.io.OutputStream; 35 | import java.util.Optional; 36 | 37 | /** 38 | * Message body handler for protobuf {@link Message}s. 39 | * 40 | * @param The body type 41 | * @since 4.0.0 42 | * @author Jonas Konrad 43 | */ 44 | @Singleton 45 | @Produces({ProtobufferCodec.PROTOBUFFER_ENCODED, ProtobufferCodec.PROTOBUFFER_ENCODED2}) 46 | @Consumes({ProtobufferCodec.PROTOBUFFER_ENCODED, ProtobufferCodec.PROTOBUFFER_ENCODED2}) 47 | public final class ProtobufBodyHandler implements MessageBodyHandler { 48 | private final ProtobufferCodec codec; 49 | private final ExtensionRegistry extensionRegistry; 50 | 51 | public ProtobufBodyHandler(ProtobufferCodec codec, ExtensionRegistry extensionRegistry) { 52 | this.codec = codec; 53 | this.extensionRegistry = extensionRegistry; 54 | } 55 | 56 | @Override 57 | public T read(Argument type, MediaType mediaType, Headers httpHeaders, InputStream inputStream) throws CodecException { 58 | Message.Builder builder = getBuilder(type) 59 | .orElseThrow(() -> new CodecException("Unable to create builder")); 60 | if (type.hasTypeVariables()) { 61 | throw new IllegalStateException("Generic type arguments are not supported"); 62 | } else { 63 | try { 64 | builder.mergeFrom(inputStream, extensionRegistry); 65 | } catch (IOException e) { 66 | throw new CodecException("Failed to read protobuf", e); 67 | } 68 | return type.getType().cast(builder.build()); 69 | } 70 | } 71 | 72 | @Override 73 | public void writeTo(Argument type, MediaType mediaType, T object, MutableHeaders outgoingHeaders, OutputStream outputStream) throws CodecException { 74 | outgoingHeaders.set(HttpHeaders.CONTENT_TYPE, mediaType != null ? mediaType : ProtobufferCodec.PROTOBUFFER_ENCODED_TYPE); 75 | try { 76 | object.writeTo(outputStream); 77 | } catch (IOException e) { 78 | throw new CodecException("Failed to write protobuf", e); 79 | } 80 | } 81 | 82 | private Optional getBuilder(Argument type) { 83 | return codec.getMessageBuilder(type.getType()); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /protobuff-support/src/test/groovy/io/micronaut/BaseSpec.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.micronaut 17 | 18 | import com.google.protobuf.ExtensionRegistry 19 | import com.google.protobuf.Message 20 | import io.micronaut.context.ApplicationContext 21 | import io.micronaut.context.annotation.Factory 22 | import io.micronaut.context.annotation.Replaces 23 | import io.micronaut.http.HttpHeaders 24 | import io.micronaut.http.HttpRequest 25 | import io.micronaut.http.client.HttpClient 26 | import io.micronaut.protobuf.codec.ProtobufferCodec 27 | import io.micronaut.runtime.server.EmbeddedServer 28 | import io.micronaut.websocket.WebSocketClient 29 | import jakarta.inject.Singleton 30 | import spock.lang.AutoCleanup 31 | import spock.lang.Shared 32 | import spock.lang.Specification 33 | 34 | abstract class BaseSpec extends Specification { 35 | @Shared 36 | @AutoCleanup 37 | EmbeddedServer embeddedServer = ApplicationContext.run(EmbeddedServer, [ 38 | "micronaut.codec.protobuf.additional-types": [SampleController.MY_PROTO_ENCODED] 39 | ]) 40 | 41 | @Shared 42 | @AutoCleanup 43 | HttpClient httpClient = embeddedServer.applicationContext.createBean( 44 | HttpClient, 45 | embeddedServer.getURL() 46 | ) 47 | 48 | @Shared 49 | @AutoCleanup 50 | WebSocketClient webSocketClient = embeddedServer.applicationContext.createBean( 51 | WebSocketClient, 52 | embeddedServer.getURL() 53 | ) 54 | 55 | byte[] getMessage(String url, Class aClass, String mediaType) { 56 | return httpClient.toBlocking().retrieve( 57 | HttpRequest.GET(url) 58 | .header(ProtobufferCodec.X_PROTOBUF_MESSAGE_HEADER, aClass.name) 59 | .header(HttpHeaders.ACCEPT, mediaType), 60 | byte[].class 61 | ) 62 | } 63 | 64 | byte[] postMessage(String url, Message message, String mediaType) { 65 | return httpClient.toBlocking().retrieve( 66 | HttpRequest.POST(url, message) 67 | .header(HttpHeaders.CONTENT_TYPE, mediaType) 68 | .header(ProtobufferCodec.X_PROTOBUF_MESSAGE_HEADER, message.class.name) 69 | .header(HttpHeaders.ACCEPT, mediaType), 70 | byte[].class 71 | ) 72 | } 73 | 74 | byte[] postMessage(String url, byte[] message, String mediaType) { 75 | return httpClient.toBlocking().retrieve( 76 | HttpRequest.POST(url, message) 77 | .header(HttpHeaders.CONTENT_TYPE, mediaType) 78 | .header(ProtobufferCodec.X_PROTOBUF_MESSAGE_HEADER, message.class.name) 79 | .header(HttpHeaders.ACCEPT, mediaType), 80 | byte[].class 81 | ) 82 | } 83 | } 84 | 85 | -------------------------------------------------------------------------------- /protobuff-support/src/test/groovy/io/micronaut/ProgrammaticController.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.micronaut 17 | 18 | import com.example.wire.Example 19 | import groovy.transform.CompileStatic 20 | import io.micronaut.http.annotation.Get 21 | import io.micronaut.protobuf.codec.ProtobufferCodec 22 | import jakarta.inject.Singleton 23 | 24 | @CompileStatic 25 | @Singleton 26 | class ProgrammaticController { 27 | public static Example.GeoPoint DUBLIN = Example.GeoPoint.newBuilder() 28 | .setLat(53.350140D) 29 | .setLng(-6.266155D) 30 | .build() 31 | 32 | @Get(processes = [ProtobufferCodec.PROTOBUFFER_ENCODED, ProtobufferCodec.PROTOBUFFER_ENCODED2, "my/myAppType"]) 33 | Example.GeoPoint city() { 34 | DUBLIN 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /protobuff-support/src/test/groovy/io/micronaut/ProgrammaticControllerCreator.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.micronaut 17 | 18 | import groovy.transform.CompileStatic 19 | import io.micronaut.context.ExecutionHandleLocator 20 | import io.micronaut.web.router.DefaultRouteBuilder 21 | 22 | import jakarta.inject.Inject 23 | import jakarta.inject.Singleton 24 | 25 | @CompileStatic 26 | @Singleton 27 | class ProgrammaticControllerCreator extends DefaultRouteBuilder { 28 | ProgrammaticControllerCreator(ExecutionHandleLocator executionHandleLocator, UriNamingStrategy uriNamingStrategy) { 29 | super(executionHandleLocator, uriNamingStrategy) 30 | } 31 | 32 | @Inject 33 | void issuesRoutes(ProgrammaticController controller) { 34 | GET("/town", controller, "city") 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /protobuff-support/src/test/groovy/io/micronaut/ProgrammaticControllerSpec.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.micronaut 17 | 18 | import com.example.wire.Example 19 | import io.micronaut.protobuf.codec.ProtobufferCodec 20 | 21 | class ProgrammaticControllerSpec extends BaseSpec { 22 | 23 | String url = embeddedServer.getURL().toString() + '/town' 24 | 25 | void "sample city should be dublin/using programmatic controller controller"() { 26 | when: 'The message is requested from the sever=[#url]' 27 | def response = getMessage(url, Example.GeoPoint.class, ProtobufferCodec.PROTOBUFFER_ENCODED) 28 | and: 'The message is parser' 29 | Example.GeoPoint city = Example.GeoPoint.parseFrom(response) 30 | then: 'Should be Dublin' 31 | SampleController.DUBLIN == city 32 | } 33 | 34 | void "sample city should be dublin/using programmatic controller controller with second codec header"() { 35 | when: 'The message is requested from the sever=[#url]' 36 | def response = getMessage(url, Example.GeoPoint.class, ProtobufferCodec.PROTOBUFFER_ENCODED2) 37 | and: 'The message is parser' 38 | Example.GeoPoint city = Example.GeoPoint.parseFrom(response) 39 | then: 'Should be Dublin' 40 | SampleController.DUBLIN == city 41 | } 42 | 43 | void "sample city should be dublin/using programmatic controller controller with custom codec header"() { 44 | when: 'The message is requested from the sever=[#url]' 45 | def response = getMessage(url, Example.GeoPoint.class, SampleController.MY_PROTO_ENCODED) 46 | and: 'The message is parser' 47 | Example.GeoPoint city = Example.GeoPoint.parseFrom(response) 48 | then: 'Should be Dublin' 49 | SampleController.DUBLIN == city 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /protobuff-support/src/test/groovy/io/micronaut/SampleController.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.micronaut 17 | 18 | import com.example.wire.Example 19 | import groovy.transform.CompileStatic 20 | import io.micronaut.http.MediaType 21 | import io.micronaut.http.annotation.Body 22 | import io.micronaut.http.annotation.Controller 23 | import io.micronaut.http.annotation.Get 24 | import io.micronaut.http.annotation.Post 25 | import io.micronaut.protobuf.codec.ProtobufferCodec 26 | 27 | @Controller 28 | @CompileStatic 29 | class SampleController { 30 | 31 | public static final String MY_PROTO_ENCODED = "my/myAppType" 32 | public static final MediaType MY_PROTO_ENCODED_TYPE = new MediaType(MY_PROTO_ENCODED) 33 | 34 | public static Example.GeoPoint DUBLIN = Example.GeoPoint.newBuilder() 35 | .setLat(53.350140D) 36 | .setLng(-6.266155D) 37 | .build() 38 | 39 | @Get(value = "/city", processes = [ProtobufferCodec.PROTOBUFFER_ENCODED, ProtobufferCodec.PROTOBUFFER_ENCODED2, "my/myAppType"]) 40 | Example.GeoPoint city() { 41 | DUBLIN 42 | } 43 | 44 | @Post(value = "/nearby", processes = [ProtobufferCodec.PROTOBUFFER_ENCODED, ProtobufferCodec.PROTOBUFFER_ENCODED2, "my/myAppType"]) 45 | Example.GeoPoint suggestVisitNearBy(@Body Example.GeoPoint point) { 46 | DUBLIN 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /protobuff-support/src/test/groovy/io/micronaut/SampleWebsocketClient.groovy: -------------------------------------------------------------------------------- 1 | package io.micronaut 2 | 3 | import com.example.wire.Example 4 | import io.micronaut.http.MediaType 5 | import io.micronaut.http.annotation.Consumes 6 | import io.micronaut.http.annotation.Produces 7 | import io.micronaut.websocket.annotation.ClientWebSocket 8 | import io.micronaut.websocket.annotation.OnMessage 9 | 10 | import java.util.concurrent.ConcurrentLinkedQueue 11 | 12 | @ClientWebSocket("/ws/echo") 13 | abstract class SampleWebsocketClient implements AutoCloseable { 14 | 15 | Collection replies = new ConcurrentLinkedQueue<>() 16 | 17 | @OnMessage 18 | @Consumes(MediaType.APPLICATION_JSON) 19 | void onMessage(Example.GeoPoint message) { 20 | replies.add(message) 21 | } 22 | 23 | @Produces(MediaType.APPLICATION_JSON) 24 | abstract void sendJson(Example.GeoPoint geoPoint) 25 | } 26 | -------------------------------------------------------------------------------- /protobuff-support/src/test/groovy/io/micronaut/SampleWebsocketHandler.groovy: -------------------------------------------------------------------------------- 1 | package io.micronaut 2 | 3 | import com.example.wire.Example 4 | import io.micronaut.http.MediaType 5 | import io.micronaut.http.annotation.Body 6 | import io.micronaut.http.annotation.Consumes 7 | import io.micronaut.http.annotation.Produces 8 | import io.micronaut.websocket.WebSocketSession 9 | import io.micronaut.websocket.annotation.OnMessage 10 | import io.micronaut.websocket.annotation.ServerWebSocket 11 | 12 | @ServerWebSocket("/ws/echo") 13 | class SampleWebsocketHandler { 14 | 15 | @OnMessage 16 | @Consumes(MediaType.APPLICATION_JSON) 17 | @Produces(MediaType.APPLICATION_JSON) 18 | void onMessage(WebSocketSession session, @Body Example.GeoPoint payload) { 19 | session.sendSync(payload) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /protobuff-support/src/test/groovy/io/micronaut/SimpleHttpGetSpec.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.micronaut 17 | 18 | import com.example.wire.Example 19 | import io.micronaut.protobuf.codec.ProtobufferCodec 20 | 21 | class SimpleHttpGetSpec extends BaseSpec { 22 | 23 | String url = embeddedServer.getURL().toString() + '/city' 24 | 25 | void "sample city should be dublin/using sample controller"() { 26 | when: 'The message is requested from the sever=[#url]' 27 | def response = getMessage(url, Example.GeoPoint.class, ProtobufferCodec.PROTOBUFFER_ENCODED) 28 | and: 'The message is parser' 29 | Example.GeoPoint city = Example.GeoPoint.parseFrom(response) 30 | then: 'Should be Dublin' 31 | SampleController.DUBLIN == city 32 | } 33 | 34 | void "test second protobuff content type header"() { 35 | when: 'The message is requested from the sever=[#url]' 36 | def response = getMessage(url, Example.GeoPoint.class, ProtobufferCodec.PROTOBUFFER_ENCODED2) 37 | and: 'The message is parser' 38 | Example.GeoPoint city = Example.GeoPoint.parseFrom(response) 39 | then: 'Should be Dublin' 40 | SampleController.DUBLIN == city 41 | } 42 | 43 | void "test cutom protobuff content type header"() { 44 | when: 'The message is requested from the sever=[#url]' 45 | def response = getMessage(url, Example.GeoPoint.class, SampleController.MY_PROTO_ENCODED) 46 | and: 'The message is parser' 47 | Example.GeoPoint city = Example.GeoPoint.parseFrom(response) 48 | then: 'Should be Dublin' 49 | SampleController.DUBLIN == city 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /protobuff-support/src/test/groovy/io/micronaut/SimpleHttpPostSpec.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.micronaut 17 | 18 | import com.example.wire.Example 19 | import io.micronaut.context.annotation.Property 20 | import io.micronaut.protobuf.codec.ProtobufferCodec 21 | 22 | class SimpleHttpPostSpec extends BaseSpec { 23 | 24 | String url = embeddedServer.getURL().toString() + '/nearby' 25 | 26 | void "near by Dublin should be Dublin"() { 27 | setup: 28 | Example.GeoPoint message = SampleController.DUBLIN 29 | when: 'The message is posted to the server=[#url]' 30 | def response = postMessage(url, message, ProtobufferCodec.PROTOBUFFER_ENCODED) 31 | and: 'The message is parsed' 32 | Example.GeoPoint city = Example.GeoPoint.parseFrom(response) 33 | then: 'Should be Dublin' 34 | SampleController.DUBLIN == city 35 | 36 | when: 'The byte[] is posted to the server=[#url]' 37 | response = postMessage(url, message.toByteArray(), ProtobufferCodec.PROTOBUFFER_ENCODED) 38 | then: 'The message is parsed' 39 | Example.GeoPoint.parseFrom(response) == SampleController.DUBLIN 40 | } 41 | 42 | void "test second protobuff content type header"() { 43 | setup: 44 | Example.GeoPoint message = SampleController.DUBLIN 45 | when: 'The message is posted to the server=[#url]' 46 | def response = postMessage(url, message, ProtobufferCodec.PROTOBUFFER_ENCODED2) 47 | and: 'The message is parsed' 48 | Example.GeoPoint city = Example.GeoPoint.parseFrom(response) 49 | then: 'Should be Dublin' 50 | SampleController.DUBLIN == city 51 | 52 | when: 'The byte[] is posted to the server=[#url]' 53 | response = postMessage(url, message.toByteArray(), ProtobufferCodec.PROTOBUFFER_ENCODED2) 54 | then: 'The message is parsed' 55 | Example.GeoPoint.parseFrom(response) == SampleController.DUBLIN 56 | } 57 | 58 | void "test custom protobuff content type header"() { 59 | setup: 60 | Example.GeoPoint message = SampleController.DUBLIN 61 | when: 'The message is posted to the server=[#url]' 62 | def response = postMessage(url, message, SampleController.MY_PROTO_ENCODED) 63 | and: 'The message is parsed' 64 | Example.GeoPoint city = Example.GeoPoint.parseFrom(response) 65 | then: 'Should be Dublin' 66 | SampleController.DUBLIN == city 67 | 68 | when: 'The byte[] is posted to the server=[#url]' 69 | response = postMessage(url, message.toByteArray(), SampleController.MY_PROTO_ENCODED) 70 | then: 'The message is parsed' 71 | Example.GeoPoint.parseFrom(response) == SampleController.DUBLIN 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /protobuff-support/src/test/groovy/io/micronaut/SimpleWebsocketSpec.groovy: -------------------------------------------------------------------------------- 1 | package io.micronaut 2 | 3 | import com.example.wire.Example 4 | import reactor.core.publisher.Mono 5 | import spock.util.concurrent.PollingConditions 6 | 7 | class SimpleWebsocketSpec extends BaseSpec { 8 | 9 | String url = embeddedServer.getURL().toString() + '/ws/echo' 10 | 11 | void "test json from proto generated classes websocket exchange"() { 12 | setup: 13 | Example.GeoPoint message = SampleController.DUBLIN 14 | SampleWebsocketClient sampleWebsocketClient = Mono.from(webSocketClient.connect(SampleWebsocketClient, url)).block() 15 | PollingConditions conditions = new PollingConditions(timeout: 15, delay: 0.5) 16 | when: 'Json is sent over ws to the server=[#url]' 17 | sampleWebsocketClient.sendJson(message) 18 | then: 'Echoed message is parsed' 19 | conditions.eventually { 20 | sampleWebsocketClient.replies.contains(message) 21 | } 22 | 23 | cleanup: 24 | sampleWebsocketClient.close() 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /protobuff-support/src/test/proto/example.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package com.example.wire; 3 | 4 | import "google/protobuf/timestamp.proto"; 5 | option java_package = "com.example.wire"; 6 | option java_outer_classname = "Example"; 7 | 8 | message CurrencyAmount { 9 | string currency = 1; 10 | double amount = 2; 11 | } 12 | 13 | message PhoneNumber { 14 | string international_code = 100; 15 | string area_code = 101; 16 | string local_number = 102; 17 | } 18 | 19 | message GeoPoint { 20 | // The latitude in degrees. It must be in the range [-90.0, +90.0]. 21 | double lat = 1; 22 | 23 | // The longitude in degrees. It must be in the range [-180.0, +180.0]. 24 | double lng = 2; 25 | } 26 | -------------------------------------------------------------------------------- /protobuff-support/src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | build/test.log.txt 4 | false 5 | 6 | %d %-5p %c - %m%n 7 | 8 | 9 | 10 | 11 | 13 | 14 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | mavenCentral() 5 | } 6 | includeBuild "build-logic" 7 | } 8 | 9 | plugins { 10 | id 'io.micronaut.build.shared.settings' version '7.4.0' 11 | } 12 | 13 | enableFeaturePreview 'TYPESAFE_PROJECT_ACCESSORS' 14 | 15 | rootProject.name = 'grpc-parent' 16 | 17 | include 'grpc-bom' 18 | include 'grpc-annotation' 19 | include 'grpc-client-runtime' 20 | include 'grpc-server-runtime' 21 | include 'grpc-runtime' 22 | include 'protobuff-support' 23 | 24 | // documentation samples 25 | include 'test-suite-java' 26 | include 'test-suite-groovy' 27 | include 'test-suite-kotlin' 28 | include 'grpc-health' 29 | include 'grpc-opentracing' 30 | 31 | micronautBuild { 32 | useStandardizedProjectNames = true 33 | importMicronautCatalog() 34 | importMicronautCatalog("micronaut-reactor") 35 | importMicronautCatalog("micronaut-tracing") 36 | importMicronautCatalog("micronaut-validation") 37 | } 38 | -------------------------------------------------------------------------------- /src/main/docs/guide/client.adoc: -------------------------------------------------------------------------------- 1 | Micronaut for gRPC does not create client beans automatically for you. Instead, you must expose which client stubs your application needs using a `@Factory`. 2 | 3 | You can dependency inject a `io.grpc.ManagedChannel` into the factory. Each injected `io.grpc.ManagedChannel` will automatically be shutdown when the application shuts down. 4 | 5 | === Configuring ManagedChannel Instances 6 | 7 | The channel can be configured using properties defined under `grpc.client` by default. 8 | 9 | For example, if you wish to disable secure communication: 10 | 11 | [source,yaml] 12 | ---- 13 | grpc: 14 | client: 15 | plaintext: true 16 | max-retry-attempts: 10 17 | ---- 18 | 19 | Properties under `grpc.client` are global properties and are the defaults used unless named configuration exists under `grpc.channels.[NAME]`. 20 | 21 | Any property of the `io.grpc.netty.NettyChannelBuilder` type can be configured. 22 | 23 | Alternatively if you prefer programmatic configuration you can write a `BeanCreationListener` for example: 24 | 25 | .Configuring the NettyChannelBuilder 26 | [source,java] 27 | ---- 28 | include::grpc-server-runtime/src/test/groovy/io/micronaut/grpc/ManagedChannelBuilderListener.java[] 29 | ---- 30 | 31 | === Auto Injected Types 32 | 33 | By default, each channel will automatically be dependency injected with beans of the following types: 34 | 35 | * `io.grpc.ClientInterceptor` - Any client interceptors declared as beans 36 | * `io.grpc.NameResolver` - The configured name resolver 37 | 38 | === Creating Client Stub Beans 39 | 40 | The value of the `@GrpcChannel` annotation can be used to specify the target server, the configuration for which can also be externalized: 41 | 42 | [source,java] 43 | ---- 44 | @Factory 45 | class Clients { 46 | @Singleton 47 | GreeterGrpc.GreeterStub reactiveStub( 48 | @GrpcChannel("https://${my.server}:${my.port}") 49 | ManagedChannel channel) { 50 | return GreeterGrpc.newStub( 51 | channel 52 | ); 53 | } 54 | } 55 | ---- 56 | 57 | The above example requires that `my.server` and `my.port` are specified in `application.yml` (or via environment variables `MY_SERVER` and `MY_PORT`). You can also externalize this further into configuration and provide channel specific configuration. 58 | 59 | For example given the following configuration: 60 | 61 | [source,yaml] 62 | ---- 63 | grpc: 64 | channels: 65 | greeter: 66 | address: '${my.server}:${my.port}' 67 | plaintext: true 68 | max-retry-attempts: 10 69 | ---- 70 | 71 | You can then define the `@GrpcChannel` annotation as follows: 72 | 73 | [source,java] 74 | ---- 75 | @Singleton 76 | GreeterGrpc.GreeterStub reactiveStub( 77 | @GrpcChannel("greeter") 78 | ManagedChannel channel) { 79 | return GreeterGrpc.newStub( 80 | channel 81 | ); 82 | } 83 | ---- 84 | 85 | The ID `greeter` is used to reference the configuration for `grpc.channels.greeter`. 86 | 87 | Using service IDs in this way is the preferred way to set up gRPC clients, because it works nicely with Service Discovery (see the next section). 88 | -------------------------------------------------------------------------------- /src/main/docs/guide/gettingStarted.adoc: -------------------------------------------------------------------------------- 1 | To get started you need first create a Micronaut project. The easiest way to do this is with the Micronaut Launch: 2 | 3 | * Go to https://micronaut.io/launch/[Micronaut Launch] 4 | * Select "gRPC Application" under "Application Type" 5 | * Choose a Language / Build System etc. 6 | * Click Generate 7 | 8 | TIP: Replace `java` with `kotlin` or `groovy` to change language and the `build` flag with `maven` to use Maven instead. 9 | 10 | Or alternatively you can create a project with `curl`: 11 | 12 | [source,bash] 13 | ---- 14 | curl --location --request GET 'https://launch.micronaut.io/create/grpc/demo?lang=JAVA&build=GRADLE' --output demo.zip 15 | unzip demo.zip -d demo 16 | cd demo 17 | ---- 18 | 19 | To manually setup gRPC you can create an application: 20 | 21 | [source,bash] 22 | ---- 23 | $ mn create-app helloworld 24 | ---- 25 | 26 | Then follow the below steps depending on the build system chosen. 27 | 28 | 29 | === Configuring Gradle 30 | 31 | To configure Gradle, first apply the `com.google.protobuf` plugin: 32 | 33 | [source,groovy] 34 | ---- 35 | plugins { 36 | ... 37 | include::test-suite-java/build.gradle[tags=plugin] 38 | } 39 | 40 | ---- 41 | 42 | Then configure the gRPC and protobuf plugins: 43 | 44 | [source,groovy] 45 | ---- 46 | include::test-suite-java/build.gradle[tags=config] 47 | ---- 48 | NOTE: If you are using JDK9 or above, in order to avoid issues javax packages, provide the following compiler option to the grpc plugin to skip these stubs: `option '@generated=omit'` (added on link:https://github.com/grpc/grpc-java/releases/tag/v1.64.0[grpc-kava v1.64.0]) See discussion here: link:https://github.com/grpc/grpc-java/issues/9179[issue 9179] 49 | 50 | [source,groovy] 51 | ---- 52 | ... 53 | generateProtoTasks { 54 | all()*.plugins { 55 | grpc { 56 | option '@generated=omit' 57 | } } 58 | } 59 | ---- 60 | 61 | Use this configuration for Kotlin projects: 62 | 63 | [source,groovy] 64 | ---- 65 | include::test-suite-kotlin/build.gradle[tags=variables] 66 | 67 | dependencies { 68 | ... 69 | include::test-suite-kotlin/build.gradle[tags=dependencies] 70 | } 71 | 72 | include::test-suite-kotlin/build.gradle[tags=config] 73 | 74 | ---- 75 | 76 | Finally, add the following dependencies to your build: 77 | 78 | For gRPC servers: 79 | 80 | dependency:micronaut-grpc-server-runtime[groupId="io.micronaut.grpc"] 81 | 82 | For gRPC clients: 83 | 84 | dependency:micronaut-grpc-client-runtime[groupId="io.micronaut.grpc"] 85 | 86 | NOTE: If you wish to use gRPC standalone without the Micronaut HTTP server you should comment out the `micronaut-http-server-netty` dependency. 87 | 88 | You can then run: 89 | 90 | [source,bash] 91 | $ ./gradlew generateProto 92 | 93 | To generate the Java sources from protobuf definitions in `src/main/proto`. 94 | 95 | === Configuring Maven 96 | 97 | For Maven create a maven project first: 98 | 99 | [source,bash] 100 | ---- 101 | $ mn create-app helloworld --build 102 | ---- 103 | 104 | Then configure the Protobuf plugin appropriately: 105 | 106 | [source,xml] 107 | ---- 108 | include::test-suite-java/pom.xml[tags=plugin, indent=0] 109 | ---- 110 | 111 | You can then run: 112 | 113 | [source,bash] 114 | $ ./mvnw generate-sources 115 | 116 | To generate the Java sources from protobuf definitions in `src/main/proto`. 117 | 118 | === Defining a Protobuf File 119 | 120 | Once you have the build setup you can define a Protobuf file for your gRPC service. For example: 121 | 122 | .src/main/proto/helloworld.proto 123 | [source,protobuf] 124 | ---- 125 | include::test-suite-java/src/main/proto/helloworld.proto[] 126 | ---- 127 | 128 | TIP: With the Micronaut 1.1 or above CLI you can generate a service with `mn create-grpc-service helloworld` which will create the `proto` file and class that implements the stub. 129 | -------------------------------------------------------------------------------- /src/main/docs/guide/introduction.adoc: -------------------------------------------------------------------------------- 1 | This project allows building https://grpc.io[gRPC] servers and clients with Micronaut. 2 | 3 | Micronaut adds the following features to the gRPC experience: 4 | 5 | * Compile Time Dependency Injection (DI) and Aspect Oriented Programming (AOP) 6 | * Service Discovery and Registrations 7 | * Distributed Tracing 8 | * Cloud Native Configuration 9 | 10 | -------------------------------------------------------------------------------- /src/main/docs/guide/protocolBuffers.adoc: -------------------------------------------------------------------------------- 1 | This project also includes a module that adds the ability to encode and decode Protocol buffers messages with the Micronaut HTTP server. 2 | 3 | To use this adds the `micronaut-protobuff-support` dependency: 4 | 5 | dependency:micronaut-protobuff-support[groupId="io.micronaut.grpc"] 6 | 7 | Micronaut will now support the encoding and decoding requests / responses of type `application/x-protobuf`. 8 | -------------------------------------------------------------------------------- /src/main/docs/guide/releaseHistory.adoc: -------------------------------------------------------------------------------- 1 | For this project, you can find a list of releases (with release notes) here: 2 | 3 | https://github.com/{githubSlug}/releases[https://github.com/{githubSlug}/releases] 4 | -------------------------------------------------------------------------------- /src/main/docs/guide/repository.adoc: -------------------------------------------------------------------------------- 1 | You can find the source code of this project in this repository: 2 | 3 | https://github.com/{githubSlug}[https://github.com/{githubSlug}] -------------------------------------------------------------------------------- /src/main/docs/guide/serviceDiscovery.adoc: -------------------------------------------------------------------------------- 1 | When using `@GrpcChannel` with a service ID without explicitly configuring the address of the service will trigger gRPC's `NameResolver` and attempt to do service discovery. 2 | 3 | The default strategy for this is to use DNS based discovery. So for example you can do: 4 | 5 | [source,java] 6 | ---- 7 | @GrpcChannel("dns://greeter") 8 | ---- 9 | 10 | Where DNS has been configured to know the address of the `greeter` service. 11 | 12 | Alternatively, if you prefer to use a service discovery server then you can use integration with Micronaut service discovery. 13 | 14 | === Service Discovery with Consul 15 | 16 | You can use Micronaut's built-in service discovery features with any supported server (Consul and Eureka currently). 17 | 18 | The way in which this is done is the https://docs.micronaut.io/latest/guide/index.html#serviceDiscoveryConsul[same as a regular Micronaut service]. 19 | 20 | ==== Registering a gRPC Service with Consul 21 | 22 | To register a gRPC service with Consul first add the `micronaut-discovery-client` dependency: 23 | 24 | dependency:micronaut-discovery-client[groupId="io.micronaut.discovery", scope="runtime"] 25 | 26 | Then setup Consul correctly: 27 | 28 | [source,yaml] 29 | ---- 30 | micronaut: 31 | application: 32 | name: greeter 33 | consul: 34 | client: 35 | registration: 36 | enabled: true 37 | defaultZone: "${CONSUL_HOST:localhost}:${CONSUL_PORT:8500}" 38 | ---- 39 | 40 | When using Service Discovery, Micronaut will register the service in Consul using the name defined in `micronaut.application.name`. 41 | If the application also uses an HTTP Server (Netty, Tomcat,...), Micronaut will register the application with the same 42 | name and a different port in Consul. In case you want to use a different name for the gRPC service in Consul: 43 | 44 | [source,yaml] 45 | ---- 46 | micronaut: 47 | application: 48 | name: greeter # <1> 49 | grpc: 50 | server: 51 | instance-id: 'hello-grpc' # <2> 52 | ---- 53 | <1> The HTTP port will be registered in Consul with the name `greeter` 54 | <2> The gRPC port will be registered in Consul with the name `hello-grpc` 55 | 56 | 57 | ==== Discoverying Services via Consul 58 | 59 | To discovery services via Consul and the Micronaut `DiscoveryClient` abstraction enable Consul and gRPC service discovery: 60 | 61 | [source,yaml] 62 | ---- 63 | grpc: 64 | client: 65 | discovery: 66 | enabled: true 67 | consul: 68 | client: 69 | defaultZone: "${CONSUL_HOST:localhost}:${CONSUL_PORT:8500}" 70 | ---- 71 | 72 | Then use the value `greeter` to discover the service when injecting the channel: 73 | 74 | [source,java] 75 | ---- 76 | @Singleton 77 | @Bean 78 | GreeterGrpc.GreeterStub greeterStub( 79 | @GrpcChannel("greeter") 80 | ManagedChannel channel) { 81 | return GreeterGrpc.newStub( 82 | channel 83 | ); 84 | } 85 | ---- 86 | -------------------------------------------------------------------------------- /src/main/docs/guide/toc.yml: -------------------------------------------------------------------------------- 1 | introduction: 2 | title: Introduction 3 | releaseHistory: Release History 4 | gettingStarted: Getting Started 5 | server: gRPC Server 6 | client: gRPC Clients 7 | serviceDiscovery: Service Discovery 8 | tracing: Distributed Tracing 9 | protocolBuffers: Protocol Buffers Support 10 | repository: Repository 11 | -------------------------------------------------------------------------------- /src/main/docs/guide/tracing.adoc: -------------------------------------------------------------------------------- 1 | gRPC includes tracing based on OpenCensus, however if you wish to use Micronaut's integration with Jaeger or Zipkin you can do so by adding the following dependencies: 2 | 3 | dependency:micronaut-tracing-brave-http[scope="compile", groupId="io.micronaut.tracing"] 4 | 5 | dependency:opentracing-grpc[scope="runtime", version="0.2.1", groupId="io.opentracing.contrib"] 6 | 7 | You then need to https://micronaut-projects.github.io/micronaut-tracing/latest/guide/#jaeger[configure either Jaeger or Zipkin] appropriately. 8 | -------------------------------------------------------------------------------- /test-suite-groovy/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "groovy" 3 | alias(libs.plugins.protobuf) 4 | id "io.micronaut.build.internal.grpc-minimal-test" 5 | } 6 | 7 | // tag::variables[] 8 | ext { 9 | grpcVersion = libs.versions.managed.grpc.asProvider().get() 10 | protobufVersion = libs.versions.managed.protobuf.asProvider().get() 11 | } 12 | // end::variables[] 13 | 14 | mainClassName = "helloworld.Application" 15 | micronaut { 16 | version.set(libs.versions.micronaut.platform.get()) 17 | coreVersion.set(libs.versions.micronaut.asProvider().get()) 18 | testRuntime "spock" 19 | enableNativeImage false 20 | processing { 21 | incremental(true) 22 | annotations("helloworld.*") 23 | } 24 | } 25 | 26 | dependencies { 27 | compileOnly libs.managed.grpc.stub 28 | compileOnly libs.javax.annotation.api 29 | compileOnly mnValidation.micronaut.validation.processor 30 | 31 | implementation mnValidation.micronaut.validation 32 | implementation mn.micronaut.runtime 33 | implementation projects.micronautGrpcRuntime 34 | implementation libs.micronaut.discovery.client 35 | implementation libs.managed.grpc.services 36 | implementation libs.managed.grpc.protobuf 37 | implementation libs.managed.protobuf.java.util 38 | implementation mn.snakeyaml 39 | 40 | runtimeOnly mnLogging.logback.classic 41 | 42 | testCompileOnly libs.javax.annotation.api 43 | 44 | testImplementation mn.groovy 45 | 46 | testCompileOnly libs.managed.protobuf.java 47 | testRuntimeOnly mnLogging.logback.classic 48 | } 49 | 50 | tasks.withType(GroovyCompile) { 51 | groovyOptions.forkOptions.jvmArgs.add('-Dgroovy.parameters=true') 52 | } 53 | 54 | tasks.withType(org.gradle.jvm.tasks.Jar) { 55 | duplicatesStrategy = DuplicatesStrategy.INCLUDE 56 | } 57 | 58 | sourceSets { 59 | main { 60 | groovy { 61 | srcDirs 'build/generated/source/proto/main/grpc' 62 | srcDirs 'build/generated/source/proto/main/java' 63 | } 64 | } 65 | } 66 | 67 | protobuf { 68 | protoc { artifact = "com.google.protobuf:protoc:$protobufVersion" } 69 | plugins { 70 | grpc { artifact = "io.grpc:protoc-gen-grpc-java:$grpcVersion" } 71 | } 72 | generateProtoTasks { 73 | all()*.plugins { grpc {} } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /test-suite-groovy/src/main/groovy/helloworld/Application.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package helloworld 17 | 18 | import groovy.transform.CompileStatic 19 | import io.micronaut.runtime.Micronaut 20 | 21 | @CompileStatic 22 | class Application { 23 | static void main(String[] args) { 24 | Micronaut.build(args) 25 | .packages("helloworld") 26 | .mainClass(Application.class) 27 | .start() 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test-suite-groovy/src/main/groovy/helloworld/GreetingEndpoint.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package helloworld 17 | 18 | // tag::imports[] 19 | import groovy.transform.CompileStatic 20 | import io.grpc.stub.StreamObserver 21 | import jakarta.inject.Singleton 22 | 23 | // end::imports[] 24 | 25 | 26 | // tag::clazz[] 27 | @CompileStatic 28 | @Singleton 29 | class GreetingEndpoint extends GreeterGrpc.GreeterImplBase { // <1> 30 | 31 | final GreetingService greetingService 32 | 33 | // <2> 34 | GreetingEndpoint(GreetingService greetingService) { 35 | this.greetingService = greetingService 36 | } 37 | 38 | @Override 39 | void sayHello(HelloRequest request, StreamObserver responseObserver) { 40 | // <3> 41 | HelloReply.newBuilder().with { 42 | message = greetingService.sayHello(request.name) 43 | responseObserver.onNext(build()) 44 | responseObserver.onCompleted() 45 | } 46 | } 47 | } 48 | // end::clazz[] 49 | -------------------------------------------------------------------------------- /test-suite-groovy/src/main/groovy/helloworld/GreetingService.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package helloworld 17 | 18 | import groovy.transform.CompileStatic 19 | import jakarta.inject.Singleton 20 | 21 | @Singleton 22 | @CompileStatic 23 | class GreetingService { 24 | 25 | String sayHello(String name) { 26 | return "Hello $name" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test-suite-groovy/src/main/groovy/helloworld/HealthService.groovy: -------------------------------------------------------------------------------- 1 | package helloworld 2 | 3 | // tag::imports[] 4 | import io.grpc.health.v1.HealthCheckResponse 5 | import io.grpc.protobuf.services.HealthStatusManager 6 | import io.micronaut.core.annotation.NonNull 7 | import io.micronaut.core.annotation.Nullable 8 | import jakarta.inject.Singleton 9 | 10 | // end::imports[] 11 | 12 | // tag::clazz[] 13 | @Singleton 14 | class HealthService { 15 | 16 | private final HealthStatusManager healthStatusManager 17 | 18 | HealthService(@Nullable HealthStatusManager healthStatusManager) { 19 | this.healthStatusManager = healthStatusManager 20 | } 21 | 22 | void setStatus(@NonNull String serviceName, @NonNull HealthCheckResponse.ServingStatus status) { 23 | healthStatusManager?.setStatus(serviceName, status) 24 | } 25 | } 26 | // end::clazz[] 27 | -------------------------------------------------------------------------------- /test-suite-groovy/src/main/proto/helloworld.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The gRPC Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | syntax = "proto3"; 15 | 16 | option java_multiple_files = true; 17 | option java_package = "helloworld"; 18 | option java_outer_classname = "HelloWorldProto"; 19 | option objc_class_prefix = "HLW"; 20 | 21 | package helloworld; 22 | 23 | // The greeting service definition. 24 | service Greeter { 25 | // Sends a greeting 26 | rpc SayHello (HelloRequest) returns (HelloReply) {} 27 | } 28 | 29 | // The request message containing the user's name. 30 | message HelloRequest { 31 | string name = 1; 32 | } 33 | 34 | // The response message containing the greetings 35 | message HelloReply { 36 | string message = 1; 37 | } -------------------------------------------------------------------------------- /test-suite-groovy/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | micronaut: 2 | application: 3 | name: helloworld 4 | -------------------------------------------------------------------------------- /test-suite-groovy/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /test-suite-groovy/src/test/groovy/helloworld/GreetingEndpointSpec.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package helloworld 17 | 18 | import io.grpc.ManagedChannel 19 | import io.micronaut.context.annotation.Factory 20 | import io.micronaut.grpc.annotation.GrpcChannel 21 | import io.micronaut.grpc.server.GrpcServerChannel 22 | import io.micronaut.test.extensions.spock.annotation.MicronautTest 23 | import jakarta.inject.Inject 24 | import jakarta.inject.Singleton 25 | import spock.lang.Specification 26 | 27 | @MicronautTest 28 | class GreetingEndpointSpec extends Specification { 29 | 30 | @Inject 31 | GreeterGrpc.GreeterBlockingStub blockingStub 32 | 33 | void "test greeting endpoint"() { 34 | given: 35 | HelloRequest request = HelloRequest.newBuilder().with { 36 | name = "Fred" 37 | build() 38 | } 39 | 40 | expect: 41 | blockingStub.sayHello( 42 | request 43 | ).message == 'Hello Fred' 44 | } 45 | 46 | @Factory 47 | static class Clients { 48 | @Singleton 49 | GreeterGrpc.GreeterBlockingStub blockingStub( 50 | @GrpcChannel(GrpcServerChannel.NAME) ManagedChannel channel) { 51 | GreeterGrpc.newBlockingStub( 52 | channel 53 | ) 54 | } 55 | } 56 | } 57 | 58 | -------------------------------------------------------------------------------- /test-suite-groovy/src/test/groovy/helloworld/HealthCheckSpec.groovy: -------------------------------------------------------------------------------- 1 | package helloworld 2 | 3 | import io.grpc.Channel 4 | import io.grpc.health.v1.HealthCheckRequest 5 | import io.grpc.health.v1.HealthCheckResponse 6 | import io.grpc.health.v1.HealthGrpc 7 | import io.grpc.protobuf.services.HealthStatusManager 8 | import io.micronaut.context.annotation.Factory 9 | import io.micronaut.grpc.annotation.GrpcChannel 10 | import io.micronaut.grpc.server.GrpcServerChannel 11 | import io.micronaut.test.extensions.spock.annotation.MicronautTest 12 | import jakarta.inject.Inject 13 | import jakarta.inject.Singleton 14 | import spock.lang.Specification 15 | 16 | @MicronautTest 17 | class HealthCheckSpec extends Specification { 18 | 19 | @Inject 20 | HealthGrpc.HealthBlockingStub healthStub 21 | 22 | @Inject 23 | HealthService healthService 24 | 25 | void "test health check"() { 26 | when: 27 | def result = healthStub.check(HealthCheckRequest.newBuilder().build()) 28 | 29 | then: 30 | result.status == HealthCheckResponse.ServingStatus.SERVING 31 | 32 | when: 33 | healthService.setStatus(HealthStatusManager.SERVICE_NAME_ALL_SERVICES, HealthCheckResponse.ServingStatus.NOT_SERVING) 34 | result = healthStub.check(HealthCheckRequest.newBuilder().build()) 35 | 36 | then: 37 | result.status == HealthCheckResponse.ServingStatus.NOT_SERVING 38 | } 39 | 40 | @Factory 41 | static class Clients { 42 | 43 | @Singleton 44 | HealthGrpc.HealthBlockingStub healthStub(@GrpcChannel(GrpcServerChannel.NAME) Channel channel) { 45 | HealthGrpc.newBlockingStub(channel) 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /test-suite-java/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/micronaut-projects/micronaut-grpc/165833157056e97eb0313098a3c2ac7b115cc0ad/test-suite-java/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /test-suite-java/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip 18 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar 19 | -------------------------------------------------------------------------------- /test-suite-java/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "java" 3 | // tag::plugin[] 4 | alias(libs.plugins.protobuf) 5 | // end::plugin[] 6 | id "io.micronaut.build.internal.grpc-tests" 7 | } 8 | 9 | // tag::variables[] 10 | ext { 11 | grpcVersion = libs.versions.managed.grpc.asProvider().get() 12 | protobufVersion = libs.versions.managed.protobuf.asProvider().get() 13 | } 14 | // end::variables[] 15 | 16 | mainClassName = "helloworld.Application" 17 | micronaut { 18 | version.set(libs.versions.micronaut.platform.get()) 19 | coreVersion.set(libs.versions.micronaut.asProvider().get()) 20 | testRuntime "junit5" 21 | enableNativeImage false 22 | processing { 23 | incremental(true) 24 | annotations("helloworld.*") 25 | } 26 | } 27 | 28 | dependencies { 29 | 30 | annotationProcessor mnValidation.micronaut.validation.processor 31 | 32 | compileOnly libs.managed.grpc.stub 33 | compileOnly libs.javax.annotation.api 34 | 35 | implementation mnValidation.micronaut.validation 36 | implementation mn.micronaut.runtime 37 | implementation projects.micronautGrpcServerRuntime 38 | implementation libs.micronaut.discovery.client 39 | implementation libs.managed.grpc.services 40 | implementation mn.snakeyaml 41 | 42 | runtimeOnly mnLogging.logback.classic 43 | 44 | testAnnotationProcessor mnValidation.micronaut.validation.processor 45 | 46 | testCompileOnly libs.javax.annotation.api 47 | testRuntimeOnly mnLogging.logback.classic 48 | testCompileOnly libs.managed.protobuf.java 49 | 50 | } 51 | 52 | // tag::config[] 53 | 54 | sourceSets { 55 | main { 56 | java { 57 | srcDirs 'build/generated/source/proto/main/grpc' 58 | srcDirs 'build/generated/source/proto/main/java' 59 | } 60 | } 61 | } 62 | 63 | protobuf { 64 | protoc { artifact = "com.google.protobuf:protoc:$protobufVersion" } 65 | plugins { 66 | grpc { artifact = "io.grpc:protoc-gen-grpc-java:$grpcVersion" } 67 | } 68 | generateProtoTasks { 69 | all()*.plugins { grpc {} } 70 | } 71 | } 72 | // end::config[] 73 | -------------------------------------------------------------------------------- /test-suite-java/src/main/java/helloworld/Application.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package helloworld; 17 | 18 | import io.micronaut.runtime.Micronaut; 19 | 20 | public class Application { 21 | 22 | public static void main(String[] args) { 23 | Micronaut.build(args) 24 | .packages("helloworld") 25 | .mainClass(Application.class) 26 | .start(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test-suite-java/src/main/java/helloworld/GreetingEndpoint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package helloworld; 17 | 18 | // tag::imports[] 19 | import io.grpc.stub.StreamObserver; 20 | 21 | import jakarta.inject.Singleton; 22 | // end::imports[] 23 | 24 | // tag::clazz[] 25 | @Singleton 26 | public class GreetingEndpoint extends GreeterGrpc.GreeterImplBase { // <1> 27 | 28 | private final GreetingService greetingService; 29 | 30 | // <2> 31 | public GreetingEndpoint(GreetingService greetingService) { 32 | this.greetingService = greetingService; 33 | } 34 | 35 | @Override 36 | public void sayHello(HelloRequest request, StreamObserver responseObserver) { 37 | // <3> 38 | final String message = greetingService.sayHello(request.getName()); 39 | HelloReply reply = HelloReply.newBuilder().setMessage(message).build(); 40 | responseObserver.onNext(reply); 41 | responseObserver.onCompleted(); 42 | } 43 | } 44 | // end::clazz[] 45 | -------------------------------------------------------------------------------- /test-suite-java/src/main/java/helloworld/GreetingService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package helloworld; 17 | 18 | import jakarta.inject.Singleton; 19 | 20 | @Singleton 21 | public class GreetingService { 22 | 23 | String sayHello(String name) { 24 | return "Hello " + name; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test-suite-java/src/main/java/helloworld/HealthService.java: -------------------------------------------------------------------------------- 1 | package helloworld; 2 | 3 | // tag::imports[] 4 | import io.grpc.health.v1.HealthCheckResponse; 5 | import io.grpc.protobuf.services.HealthStatusManager; 6 | import io.micronaut.core.annotation.NonNull; 7 | import io.micronaut.core.annotation.Nullable; 8 | 9 | import jakarta.inject.Singleton; 10 | // end::imports[] 11 | 12 | // tag::clazz[] 13 | @Singleton 14 | public class HealthService { 15 | 16 | private final HealthStatusManager healthStatusManager; 17 | 18 | public HealthService(@Nullable HealthStatusManager healthStatusManager) { 19 | this.healthStatusManager = healthStatusManager; 20 | } 21 | 22 | public void setStatus(@NonNull String serviceName, @NonNull HealthCheckResponse.ServingStatus status) { 23 | if (healthStatusManager != null) { 24 | healthStatusManager.setStatus(serviceName, status); 25 | } 26 | } 27 | } 28 | // end::clazz[] 29 | -------------------------------------------------------------------------------- /test-suite-java/src/main/proto/helloworld.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The gRPC Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | syntax = "proto3"; 15 | 16 | option java_multiple_files = true; 17 | option java_package = "helloworld"; 18 | option java_outer_classname = "HelloWorldProto"; 19 | option objc_class_prefix = "HLW"; 20 | 21 | package helloworld; 22 | 23 | // The greeting service definition. 24 | service Greeter { 25 | // Sends a greeting 26 | rpc SayHello (HelloRequest) returns (HelloReply) {} 27 | } 28 | 29 | // The request message containing the user's name. 30 | message HelloRequest { 31 | string name = 1; 32 | } 33 | 34 | // The response message containing the greetings 35 | message HelloReply { 36 | string message = 1; 37 | } -------------------------------------------------------------------------------- /test-suite-java/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | micronaut: 2 | application: 3 | name: hello-world-java 4 | # Uncomment to enable service discovery with Consul 5 | #consul: 6 | # client: 7 | # registration: 8 | # enabled: true 9 | # defaultZone: "${CONSUL_HOST:localhost}:${CONSUL_PORT:8500}" 10 | -------------------------------------------------------------------------------- /test-suite-java/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /test-suite-java/src/test/java/helloworld/GreetingEndpointTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package helloworld; 17 | // tag::imports[] 18 | 19 | import io.grpc.ManagedChannel; 20 | import io.micronaut.context.annotation.Bean; 21 | import io.micronaut.context.annotation.Factory; 22 | import io.micronaut.grpc.annotation.GrpcChannel; 23 | import io.micronaut.grpc.server.GrpcServerChannel; 24 | import io.micronaut.test.extensions.junit5.annotation.MicronautTest; 25 | 26 | import org.junit.jupiter.api.Test; 27 | 28 | import jakarta.inject.Inject; 29 | 30 | import static org.junit.jupiter.api.Assertions.assertEquals; 31 | // end::imports[] 32 | 33 | // tag::test[] 34 | @MicronautTest // <1> 35 | class GreetingEndpointTest { 36 | 37 | @Inject 38 | GreeterGrpc.GreeterBlockingStub blockingStub; // <2> 39 | 40 | @Test 41 | void testHelloWorld() { 42 | final HelloRequest request = HelloRequest.newBuilder() // <3> 43 | .setName("Fred") 44 | .build(); 45 | assertEquals( 46 | "Hello Fred", 47 | blockingStub.sayHello(request) 48 | .getMessage() 49 | ); 50 | } 51 | 52 | } 53 | // end::test[] 54 | 55 | // tag::clients[] 56 | @Factory 57 | class Clients { 58 | 59 | @Bean 60 | GreeterGrpc.GreeterBlockingStub blockingStub( 61 | @GrpcChannel(GrpcServerChannel.NAME) ManagedChannel channel) { // <1> 62 | return GreeterGrpc.newBlockingStub( // <2> 63 | channel 64 | ); 65 | } 66 | } 67 | // end::clients[] 68 | -------------------------------------------------------------------------------- /test-suite-java/src/test/java/helloworld/HealthCheckTest.java: -------------------------------------------------------------------------------- 1 | package helloworld; 2 | 3 | import io.grpc.ManagedChannel; 4 | import io.grpc.health.v1.HealthCheckRequest; 5 | import io.grpc.health.v1.HealthCheckResponse; 6 | import io.grpc.health.v1.HealthGrpc; 7 | import io.grpc.protobuf.services.HealthStatusManager; 8 | import io.micronaut.context.annotation.Bean; 9 | import io.micronaut.context.annotation.Factory; 10 | import io.micronaut.grpc.annotation.GrpcChannel; 11 | import io.micronaut.grpc.server.GrpcServerChannel; 12 | import io.micronaut.test.extensions.junit5.annotation.MicronautTest; 13 | 14 | import org.junit.jupiter.api.Test; 15 | 16 | import jakarta.inject.Inject; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertEquals; 19 | 20 | @MicronautTest 21 | class HealthCheckTest { 22 | 23 | @Inject 24 | HealthGrpc.HealthBlockingStub healthStub; 25 | 26 | @Inject 27 | HealthService healthService; 28 | 29 | @Test 30 | void testHealth() { 31 | assertEquals( 32 | HealthCheckResponse.ServingStatus.SERVING, 33 | healthStub.check(HealthCheckRequest.newBuilder().build()).getStatus() 34 | ); 35 | 36 | healthService.setStatus(HealthStatusManager.SERVICE_NAME_ALL_SERVICES, HealthCheckResponse.ServingStatus.NOT_SERVING); 37 | 38 | assertEquals( 39 | HealthCheckResponse.ServingStatus.NOT_SERVING, 40 | healthStub.check(HealthCheckRequest.newBuilder().build()).getStatus() 41 | ); 42 | } 43 | 44 | @Factory 45 | static class HealthClients { 46 | 47 | @Bean 48 | HealthGrpc.HealthBlockingStub blockingStub(@GrpcChannel(GrpcServerChannel.NAME) ManagedChannel channel) { 49 | return HealthGrpc.newBlockingStub(channel); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /test-suite-kotlin/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | alias(mn.plugins.kotlin.jvm) 3 | alias(mn.plugins.kotlin.kapt) 4 | alias(mn.plugins.kotlin.allopen) 5 | alias(libs.plugins.protobuf) 6 | id "io.micronaut.build.internal.grpc-minimal-test" 7 | } 8 | 9 | kotlin { 10 | jvmToolchain { 11 | languageVersion.set(JavaLanguageVersion.of(17)) 12 | } 13 | } 14 | 15 | // tag::variables[] 16 | ext { 17 | grpcVersion = libs.versions.managed.grpc.asProvider().get() 18 | grpcKotlinVersion = libs.versions.managed.grpc.kotlin.get() 19 | protobufVersion = libs.versions.managed.protobuf.asProvider().get() 20 | } 21 | // end::variables[] 22 | 23 | mainClassName = "helloworld.Application" 24 | micronaut { 25 | version.set(libs.versions.micronaut.platform.get()) 26 | coreVersion.set(libs.versions.micronaut.asProvider().get()) 27 | testRuntime "junit5" 28 | enableNativeImage false 29 | processing { 30 | incremental(true) 31 | annotations("helloworld.*") 32 | } 33 | } 34 | 35 | dependencies { 36 | kapt mnValidation.micronaut.validation.processor 37 | implementation mnValidation.micronaut.validation 38 | implementation mn.kotlin.stdlib.jdk8 39 | implementation mn.kotlin.reflect 40 | implementation mn.kotlinx.coroutines.core 41 | implementation mn.micronaut.runtime 42 | implementation projects.micronautGrpcServerRuntime 43 | implementation mn.snakeyaml 44 | 45 | // tag::dependencies[] 46 | implementation libs.managed.grpc.kotlin.stub 47 | implementation libs.managed.grpc.services 48 | compileOnly libs.managed.grpc.stub 49 | compileOnly libs.managed.protobuf.java 50 | compileOnly libs.javax.annotation.api 51 | // end::dependencies[] 52 | 53 | runtimeOnly mnLogging.logback.classic 54 | runtimeOnly mn.jackson.module.kotlin 55 | 56 | testCompileOnly libs.javax.annotation.api 57 | 58 | testRuntimeOnly mnLogging.logback.classic 59 | testRuntimeOnly mn.jackson.module.kotlin 60 | } 61 | 62 | // tag::config[] 63 | 64 | sourceSets { 65 | main { 66 | java { 67 | srcDirs 'build/generated/source/proto/main/grpc' 68 | srcDirs 'build/generated/source/proto/main/grpckt' 69 | srcDirs 'build/generated/source/proto/main/java' 70 | } 71 | } 72 | } 73 | 74 | protobuf { 75 | protoc { artifact = "com.google.protobuf:protoc:$protobufVersion" } 76 | plugins { 77 | grpc { artifact = "io.grpc:protoc-gen-grpc-java:$grpcVersion" } 78 | grpckt { artifact = "io.grpc:protoc-gen-grpc-kotlin:${grpcKotlinVersion}:jdk8@jar" } 79 | } 80 | generateProtoTasks { 81 | all()*.plugins { 82 | grpc {} 83 | grpckt {} 84 | } 85 | } 86 | } 87 | 88 | // end::config[] 89 | -------------------------------------------------------------------------------- /test-suite-kotlin/src/main/kotlin/helloworld/Application.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package helloworld 17 | 18 | import io.micronaut.runtime.Micronaut 19 | 20 | object Application { 21 | 22 | @JvmStatic 23 | fun main(args: Array) { 24 | Micronaut.build() 25 | .packages("helloworld") 26 | .mainClass(Application.javaClass) 27 | .start() 28 | } 29 | } -------------------------------------------------------------------------------- /test-suite-kotlin/src/main/kotlin/helloworld/GreetingEndpoint.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package helloworld 17 | 18 | // tag::imports[] 19 | import jakarta.inject.Singleton 20 | // end::imports[] 21 | 22 | // tag::clazz[] 23 | @Singleton // <1> 24 | class GreetingEndpoint(val greetingService: GreetingService) : GreeterGrpcKt.GreeterCoroutineImplBase() { // <2> 25 | override suspend fun sayHello(request: HelloRequest): HelloReply { 26 | // <3> 27 | val message = greetingService.sayHello(request.name) 28 | val reply = HelloReply.newBuilder().setMessage(message).build() 29 | return reply 30 | } 31 | } 32 | // end::clazz[] 33 | -------------------------------------------------------------------------------- /test-suite-kotlin/src/main/kotlin/helloworld/GreetingService.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package helloworld 17 | 18 | import jakarta.inject.Singleton 19 | 20 | @Singleton 21 | class GreetingService { 22 | 23 | fun sayHello(name: String): String { 24 | return "Hello $name" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test-suite-kotlin/src/main/kotlin/helloworld/HealthService.kt: -------------------------------------------------------------------------------- 1 | package helloworld 2 | 3 | // tag::imports[] 4 | import io.grpc.health.v1.HealthCheckResponse.ServingStatus 5 | import io.grpc.protobuf.services.HealthStatusManager 6 | import jakarta.inject.Singleton 7 | // end::imports[] 8 | 9 | // tag::clazz[] 10 | @Singleton 11 | class HealthService(private val healthStatusManager: HealthStatusManager?) { 12 | 13 | fun setStatus(serviceName: String, status: ServingStatus) { 14 | healthStatusManager?.setStatus(serviceName, status) 15 | } 16 | } 17 | // end::clazz[] 18 | -------------------------------------------------------------------------------- /test-suite-kotlin/src/main/proto/helloworld.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The gRPC Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | syntax = "proto3"; 15 | 16 | option java_multiple_files = true; 17 | option java_package = "helloworld"; 18 | option java_outer_classname = "HelloWorldProto"; 19 | option objc_class_prefix = "HLW"; 20 | 21 | package helloworld; 22 | 23 | // The greeting service definition. 24 | service Greeter { 25 | // Sends a greeting 26 | rpc SayHello (HelloRequest) returns (HelloReply) {} 27 | } 28 | 29 | // The request message containing the user's name. 30 | message HelloRequest { 31 | string name = 1; 32 | } 33 | 34 | // The response message containing the greetings 35 | message HelloReply { 36 | string message = 1; 37 | } -------------------------------------------------------------------------------- /test-suite-kotlin/src/main/resources/META-INF/native-image/io.micronaut.grpc/kotlin-runtime/native-image.properties: -------------------------------------------------------------------------------- 1 | # This comes in the next minor update of micronaut-kotlin, but we're still on 4.0.x here 2 | Args = --initialize-at-build-time=kotlin.annotation.AnnotationRetention,kotlin.annotation.AnnotationTarget,kotlin.coroutines.intrinsics.CoroutineSingletons 3 | -------------------------------------------------------------------------------- /test-suite-kotlin/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | micronaut: 2 | application: 3 | name: helloworld 4 | # Uncomment to enable service discovery with Consul 5 | #consul: 6 | # client: 7 | # registration: 8 | # enabled: true 9 | # defaultZone: "${CONSUL_HOST:localhost}:${CONSUL_PORT:8500}" 10 | -------------------------------------------------------------------------------- /test-suite-kotlin/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /test-suite-kotlin/src/test/kotlin/helloworld/GreetingServiceTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-2019 original authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package helloworld 17 | 18 | import io.grpc.ManagedChannel 19 | import io.micronaut.context.annotation.Factory 20 | import io.micronaut.grpc.annotation.GrpcChannel 21 | import io.micronaut.grpc.server.GrpcServerChannel 22 | import io.micronaut.test.extensions.junit5.annotation.MicronautTest 23 | import jakarta.inject.Inject 24 | import jakarta.inject.Singleton 25 | import kotlinx.coroutines.runBlocking 26 | import org.junit.jupiter.api.Assertions 27 | import org.junit.jupiter.api.Test 28 | 29 | @MicronautTest 30 | class GreetingServiceTest { 31 | 32 | @Inject 33 | lateinit var greetingClient: GreeterGrpcKt.GreeterCoroutineStub 34 | 35 | @Test 36 | fun testGreetingService() = runBlocking { 37 | Assertions.assertEquals( 38 | "Hello John", 39 | greetingClient.sayHello( 40 | HelloRequest.newBuilder().setName("John").build() 41 | ).message 42 | ) 43 | } 44 | } 45 | 46 | @Factory 47 | class Clients { 48 | @Singleton 49 | fun greetingClient(@GrpcChannel(GrpcServerChannel.NAME) channel: ManagedChannel): GreeterGrpcKt.GreeterCoroutineStub { 50 | return GreeterGrpcKt.GreeterCoroutineStub( 51 | channel 52 | ) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /test-suite-kotlin/src/test/kotlin/helloworld/HealthCheckTest.kt: -------------------------------------------------------------------------------- 1 | package helloworld 2 | 3 | import io.grpc.Channel 4 | import io.grpc.health.v1.HealthCheckRequest 5 | import io.grpc.health.v1.HealthCheckResponse 6 | import io.grpc.health.v1.HealthGrpc 7 | import io.grpc.protobuf.services.HealthStatusManager 8 | import io.micronaut.context.annotation.Factory 9 | import io.micronaut.grpc.annotation.GrpcChannel 10 | import io.micronaut.grpc.server.GrpcServerChannel 11 | import io.micronaut.test.extensions.junit5.annotation.MicronautTest 12 | import jakarta.inject.Inject 13 | import jakarta.inject.Singleton 14 | import kotlinx.coroutines.runBlocking 15 | import org.junit.jupiter.api.Assertions 16 | import org.junit.jupiter.api.Test 17 | 18 | @MicronautTest 19 | class HealthCheckTest { 20 | 21 | @Inject 22 | lateinit var healthStub: HealthGrpc.HealthBlockingStub 23 | 24 | @Inject 25 | lateinit var healthService: HealthService 26 | 27 | @Test 28 | fun testHealthCheck() = runBlocking { 29 | Assertions.assertEquals( 30 | HealthCheckResponse.ServingStatus.SERVING, 31 | healthStub.check(HealthCheckRequest.newBuilder().build()).status 32 | ) 33 | 34 | healthService.setStatus( 35 | HealthStatusManager.SERVICE_NAME_ALL_SERVICES, 36 | HealthCheckResponse.ServingStatus.NOT_SERVING 37 | ) 38 | 39 | Assertions.assertEquals( 40 | HealthCheckResponse.ServingStatus.NOT_SERVING, 41 | healthStub.check(HealthCheckRequest.newBuilder().build()).status 42 | ) 43 | } 44 | } 45 | 46 | @Factory 47 | class HealthClient { 48 | 49 | @Singleton 50 | fun healthClient(@GrpcChannel(GrpcServerChannel.NAME) channel: Channel): HealthGrpc.HealthBlockingStub = 51 | HealthGrpc.newBlockingStub(channel) 52 | } 53 | --------------------------------------------------------------------------------