├── .github ├── badges │ ├── jacoco.svg │ └── udemy.svg └── workflows │ ├── gradle.yml │ ├── lint.yml │ └── udemy.yml ├── .gitignore ├── .idea ├── .gitignore ├── compiler.xml ├── jarRepositories.xml ├── misc.xml ├── runConfigurations │ ├── All_in_grpc_java_course_test.xml │ ├── BlogClient.xml │ ├── BlogDb.xml │ ├── BlogServer.xml │ ├── CalculatorClient_avg.xml │ ├── CalculatorClient_max.xml │ ├── CalculatorClient_primes.xml │ ├── CalculatorClient_sqrt.xml │ ├── CalculatorClient_sum.xml │ ├── CalculatorServer.xml │ ├── GreetingClientTls.xml │ ├── GreetingClient_greet.xml │ ├── GreetingClient_greet_everyone.xml │ ├── GreetingClient_greet_many_times.xml │ ├── GreetingClient_greet_with_deadline.xml │ ├── GreetingClient_long_greet.xml │ ├── GreetingServer.xml │ └── GreetingServerTls.xml └── vcs.xml ├── .protolint └── .protolint.yaml ├── README.md ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle ├── src ├── main │ ├── java │ │ ├── blog │ │ │ ├── client │ │ │ │ └── BlogClient.java │ │ │ ├── docker-compose.yml │ │ │ └── server │ │ │ │ ├── BlogServer.java │ │ │ │ └── BlogServiceImpl.java │ │ ├── calculator │ │ │ ├── client │ │ │ │ └── CalculatorClient.java │ │ │ └── server │ │ │ │ ├── CalculatorServer.java │ │ │ │ └── CalculatorServiceImpl.java │ │ ├── greeting │ │ │ ├── client │ │ │ │ ├── AddHeaderInterceptor.java │ │ │ │ ├── GreetingClient.java │ │ │ │ ├── GreetingClientTls.java │ │ │ │ └── LogInterceptor.java │ │ │ └── server │ │ │ │ ├── GreetingServer.java │ │ │ │ ├── GreetingServerTls.java │ │ │ │ ├── GreetingServiceImpl.java │ │ │ │ ├── HeaderCheckInterceptor.java │ │ │ │ ├── LogInterceptor.java │ │ │ │ └── SleeperImpl.java │ │ └── utils │ │ │ ├── ExcludeFromJacocoGeneratedReport.java │ │ │ └── Sleeper.java │ └── proto │ │ ├── blog │ │ └── blog.proto │ │ ├── calculator │ │ ├── avg.proto │ │ ├── calculator.proto │ │ ├── max.proto │ │ ├── prime.proto │ │ ├── sqrt.proto │ │ └── sum.proto │ │ ├── dummy.proto │ │ └── greeting │ │ └── greeting.proto └── test │ └── java │ ├── blog │ ├── client │ │ ├── BlogCreateTest.java │ │ ├── BlogDeleteTest.java │ │ ├── BlogListTest.java │ │ ├── BlogReadTest.java │ │ └── BlogUpdateTest.java │ └── server │ │ ├── BlogCreateTest.java │ │ ├── BlogDeleteTest.java │ │ ├── BlogListTest.java │ │ ├── BlogReadTest.java │ │ └── BlogUpdateTest.java │ ├── calculator │ └── server │ │ ├── CalculatorAvgTest.java │ │ ├── CalculatorMaxTest.java │ │ ├── CalculatorPrimesTest.java │ │ ├── CalculatorSqrtTest.java │ │ └── CalculatorSumTest.java │ ├── greeting │ └── server │ │ ├── GreetingEveryoneServerTest.java │ │ ├── GreetingManyTimesServerTest.java │ │ ├── GreetingServerTest.java │ │ ├── GreetingWithDeadlineTest.java │ │ ├── GreetingWithDeadlineThrowsTest.java │ │ ├── InterceptorTest.java │ │ └── LongGreetingServerTest.java │ └── utils │ ├── ClientTestBase.java │ ├── ServerTestBase.java │ └── StubInstantiator.java └── ssl ├── README.md └── ssl.sh /.github/badges/jacoco.svg: -------------------------------------------------------------------------------- 1 | coverage100% -------------------------------------------------------------------------------- /.github/badges/udemy.svg: -------------------------------------------------------------------------------- 1 | udemy4.43 -------------------------------------------------------------------------------- /.github/workflows/gradle.yml: -------------------------------------------------------------------------------- 1 | name: build master branch 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | paths: 7 | - "src/**/*.java" 8 | - "*.gradle" 9 | - "gradle-*" 10 | pull_request: 11 | branches: [ master ] 12 | paths: 13 | - "src/**/*.java" 14 | - "*.gradle" 15 | - "gradle-*" 16 | 17 | jobs: 18 | build: 19 | runs-on: ${{ matrix.os }} 20 | 21 | strategy: 22 | matrix: 23 | os: [windows-2022, ubuntu-20.04, macos-11] 24 | jdk: ['8', '17'] 25 | steps: 26 | - uses: actions/checkout@v2 27 | - name: Set up JDK 28 | uses: actions/setup-java@v2 29 | with: 30 | java-version: ${{ matrix.jdk }} 31 | distribution: 'temurin' 32 | cache: gradle 33 | 34 | - name: Build with Gradle 35 | uses: gradle/gradle-build-action@937999e9cc2425eddc7fd62d1053baf041147db7 36 | with: 37 | arguments: build -x test --no-daemon 38 | 39 | codecov: 40 | 41 | runs-on: ubuntu-latest 42 | needs: build 43 | steps: 44 | - uses: actions/checkout@v2 45 | - name: Run Test Coverage 46 | run: ./gradlew jacocoTestReport 47 | 48 | - name: Generate JaCoCo Badge 49 | uses: cicirello/jacoco-badge-generator@v2 50 | with: 51 | jacoco-csv-file: build/reports/jacoco/jacoco.csv 52 | 53 | - name: Commit the badge (if it changed) 54 | run: | 55 | if [[ `git status --porcelain` ]]; then 56 | git config --global user.name 'Clement Jean' 57 | git config --global user.email 'Clement-Jean@users.noreply.github.com' 58 | git add .github/badges/jacoco.svg 59 | git commit -m "Autogenerated JaCoCo coverage badge" 60 | git push 61 | fi -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: lint protocol buffers 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | paths: 7 | - "src/**/*.proto" 8 | pull_request: 9 | branches: [ master ] 10 | paths: 11 | - "src/**/*.proto" 12 | 13 | jobs: 14 | pr-check: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Checkout source 18 | uses: actions/checkout@v1 19 | 20 | - name: Run protolint 21 | uses: plexsystems/protolint-action@v0.4.0 22 | with: 23 | configDirectory: .protolint -------------------------------------------------------------------------------- /.github/workflows/udemy.yml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_dispatch: 3 | schedule: 4 | - cron: '0 0 1 * *' 5 | 6 | jobs: 7 | generate_badge: 8 | runs-on: ubuntu-latest 9 | name: badge_generation 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v2 13 | - name: Badge generation 14 | uses: Clement-Jean/udemy-badge-generator@v1 15 | env: 16 | UDEMY_TOKEN: ${{ secrets.UDEMY_TOKEN }} 17 | UDEMY_COURSE: ${{ secrets.UDEMY_COURSE }} 18 | - name: Commit the badge (if it changed) 19 | run: | 20 | if [[ `git status --porcelain` ]]; then 21 | git config --global user.name 'Clement Jean' 22 | git config --global user.email 'Clement-Jean@users.noreply.github.com' 23 | git add .github/badges/udemy.svg 24 | git commit -m "Autogenerated Udemy rating badge" 25 | git push 26 | fi 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## IDEA IntelliJ 2 | 3 | # User-specific stuff 4 | .idea/**/workspace.xml 5 | .idea/**/tasks.xml 6 | .idea/**/usage.statistics.xml 7 | .idea/**/dictionaries 8 | .idea/**/shelf 9 | 10 | # Generated files 11 | .idea/**/contentModel.xml 12 | 13 | # Sensitive or high-churn files 14 | .idea/**/dataSources/ 15 | .idea/**/dataSources.ids 16 | .idea/**/dataSources.local.xml 17 | .idea/**/sqlDataSources.xml 18 | .idea/**/dynamic.xml 19 | .idea/**/uiDesigner.xml 20 | .idea/**/dbnavigator.xml 21 | 22 | # Gradle 23 | .idea/**/gradle.xml 24 | .idea/**/libraries 25 | 26 | # File-based project format 27 | *.iws 28 | 29 | # IntelliJ 30 | out/ 31 | 32 | ## JAVA 33 | 34 | # Compiled class file 35 | *.class 36 | 37 | # Log file 38 | *.log 39 | 40 | # Package Files # 41 | *.jar 42 | *.war 43 | *.nar 44 | *.ear 45 | *.zip 46 | *.tar.gz 47 | *.rar 48 | 49 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 50 | hs_err_pid* 51 | replay_pid* 52 | 53 | 54 | ## Gradle 55 | 56 | .gradle 57 | **/build/ 58 | !src/**/build/ 59 | 60 | # Ignore Gradle GUI config 61 | gradle-app.setting 62 | 63 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 64 | !gradle-wrapper.jar 65 | 66 | # Cache of project 67 | .gradletasknamecache 68 | 69 | # Eclipse Gradle plugin generated files 70 | # Eclipse Core 71 | .project 72 | # JDT-specific (Eclipse Java Development Tools) 73 | .classpath 74 | 75 | 76 | ## SSL 77 | *.crt 78 | *.key 79 | *.csr 80 | *.pem 81 | 82 | ## Mac 83 | .DS_Store 84 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 25 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /.idea/runConfigurations/All_in_grpc_java_course_test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | -------------------------------------------------------------------------------- /.idea/runConfigurations/BlogClient.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | -------------------------------------------------------------------------------- /.idea/runConfigurations/BlogDb.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/runConfigurations/BlogServer.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | -------------------------------------------------------------------------------- /.idea/runConfigurations/CalculatorClient_avg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | -------------------------------------------------------------------------------- /.idea/runConfigurations/CalculatorClient_max.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | -------------------------------------------------------------------------------- /.idea/runConfigurations/CalculatorClient_primes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | -------------------------------------------------------------------------------- /.idea/runConfigurations/CalculatorClient_sqrt.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | -------------------------------------------------------------------------------- /.idea/runConfigurations/CalculatorClient_sum.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | -------------------------------------------------------------------------------- /.idea/runConfigurations/CalculatorServer.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | -------------------------------------------------------------------------------- /.idea/runConfigurations/GreetingClientTls.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 15 | -------------------------------------------------------------------------------- /.idea/runConfigurations/GreetingClient_greet.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | -------------------------------------------------------------------------------- /.idea/runConfigurations/GreetingClient_greet_everyone.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | -------------------------------------------------------------------------------- /.idea/runConfigurations/GreetingClient_greet_many_times.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | -------------------------------------------------------------------------------- /.idea/runConfigurations/GreetingClient_greet_with_deadline.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | -------------------------------------------------------------------------------- /.idea/runConfigurations/GreetingClient_long_greet.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | -------------------------------------------------------------------------------- /.idea/runConfigurations/GreetingServer.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 15 | -------------------------------------------------------------------------------- /.idea/runConfigurations/GreetingServerTls.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 15 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.protolint/.protolint.yaml: -------------------------------------------------------------------------------- 1 | lint: 2 | directories: 3 | exclude: 4 | - build 5 | rules: 6 | no_default: false 7 | add: 8 | - ENUM_FIELD_NAMES_PREFIX 9 | - ENUM_FIELD_NAMES_UPPER_SNAKE_CASE 10 | - ENUM_FIELD_NAMES_ZERO_VALUE_END_WITH 11 | - ENUM_NAMES_UPPER_CAMEL_CASE 12 | - FILE_NAMES_LOWER_SNAKE_CASE 13 | - FIELD_NAMES_LOWER_SNAKE_CASE 14 | - MPORTS_SORTED 15 | - MESSAGE_NAMES_UPPER_CAMEL_CASE 16 | - ORDER 17 | - PACKAGE_NAME_LOWER_CASE 18 | - RPC_NAMES_UPPER_CAMEL_CASE 19 | - SERVICE_NAMES_UPPER_CAMEL_CASE 20 | - REPEATED_FIELD_NAMES_PLURALIZED 21 | - QUOTE_CONSISTENT 22 | - INDENT 23 | - PROTO3_FIELDS_AVOID_REQUIRED 24 | - PROTO3_GROUPS_AVOID 25 | - MAX_LINE_LENGTH 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gRPC Java 2 | 3 | [![build master branch](https://github.com/Clement-Jean/grpc-java-course/actions/workflows/gradle.yml/badge.svg)](https://github.com/Clement-Jean/grpc-java-course/actions/workflows/gradle.yml) ![Coverage](.github/badges/jacoco.svg) [![lint protocol buffers](https://github.com/Clement-Jean/grpc-java-course/actions/workflows/lint.yml/badge.svg)](https://github.com/Clement-Jean/grpc-java-course/actions/workflows/lint.yml) ![Udemy](.github/badges/udemy.svg) 4 | 5 | ## COUPON: `START_OCT_2025` 6 | 7 | ## Notes 8 | 9 | - The code you see in this repository might be little different from what you saw in the course videos, this is due to the fact that: 10 | - I'm testing the code 11 | - I or a student noticed an error/bug/deprecation after recording 12 | - Dependencies are evolving faster than I can rerecord 13 | 14 | I do the maximum to keep the main features and the code syntax similar by keeping the edits trivial. **However if you 15 | do get in a situation where you feel lost, leave an `issue` on the repository** 16 | - The coverage shown in the badges section only counts the classes that need testing, such as: 17 | - greeting/server/GreetingServiceImpl 18 | - calculator/server/CalculatorServiceImpl 19 | - blog/server/BlogServiceImpl 20 | - blog/client/BlogClient 21 | 22 | For more information, please check the `jacocoTestReport` task in `build.gradle` 23 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | //file:noinspection GrUnresolvedAccess 2 | //file:noinspection GroovyAssignabilityCheck 3 | plugins { 4 | id 'java' 5 | id 'idea' 6 | id 'com.google.protobuf' version '0.9.4' 7 | 8 | id 'jacoco' 9 | } 10 | 11 | group 'com.clementjean.grpc' 12 | version '1.0-SNAPSHOT' 13 | 14 | repositories { 15 | mavenCentral() 16 | } 17 | 18 | ext { 19 | protoVersion = '3.24.4' 20 | grpcVersion = '1.59.0' 21 | } 22 | 23 | protobuf { 24 | protoc { 25 | artifact = "com.google.protobuf:protoc:$protoVersion" 26 | } 27 | plugins { 28 | grpc { 29 | artifact = "io.grpc:protoc-gen-grpc-java:$grpcVersion" 30 | } 31 | } 32 | generateProtoTasks { 33 | all()*.plugins { 34 | grpc {} 35 | } 36 | } 37 | } 38 | 39 | sourceSets.main.java.srcDir new File(buildDir, 'generated/source') 40 | 41 | dependencies { 42 | runtimeOnly "io.grpc:grpc-netty-shaded:$grpcVersion" 43 | implementation "io.grpc:grpc-protobuf:$grpcVersion" 44 | implementation "io.grpc:grpc-stub:$grpcVersion" 45 | implementation "io.grpc:grpc-services:$grpcVersion" 46 | compileOnly 'org.apache.tomcat:annotations-api:6.0.53' 47 | implementation "com.google.protobuf:protobuf-java-util:$protoVersion" 48 | 49 | implementation 'org.mongodb:mongodb-driver-sync:4.11.0' 50 | 51 | testImplementation 'junit:junit:4.13.2' 52 | testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.2' 53 | testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.2' 54 | testImplementation 'org.mockito:mockito-core:5.2.0' 55 | testImplementation "io.grpc:grpc-inprocess:$grpcVersion" 56 | } 57 | 58 | test { 59 | useJUnitPlatform() 60 | finalizedBy jacocoTestReport 61 | } 62 | 63 | jacocoTestReport { 64 | dependsOn test 65 | 66 | reports { 67 | xml.required = false 68 | html.required = false 69 | csv.required = true 70 | csv.destination file("${buildDir}/reports/jacoco/jacoco.csv") 71 | } 72 | afterEvaluate { 73 | classDirectories.setFrom(files(classDirectories.files.collect { 74 | fileTree( 75 | dir: it, 76 | includes: [ 77 | "greeting/server/GreetingServiceImpl.class", 78 | "calculator/server/CalculatorServiceImpl.class", 79 | "blog/server/BlogServiceImpl.class", 80 | "blog/client/BlogClient.class", 81 | ] 82 | ) 83 | })) 84 | } 85 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clement-Jean/grpc-java-course/e4c7e468d394ab7f06829098c3ca81169e9b0ccc/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-7.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MSYS* | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /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 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'grpc-java-course' 2 | 3 | -------------------------------------------------------------------------------- /src/main/java/blog/client/BlogClient.java: -------------------------------------------------------------------------------- 1 | package blog.client; 2 | 3 | import com.google.common.annotations.VisibleForTesting; 4 | import com.google.protobuf.Empty; 5 | import com.proto.blog.Blog; 6 | import com.proto.blog.BlogId; 7 | import com.proto.blog.BlogServiceGrpc; 8 | import io.grpc.ManagedChannel; 9 | import io.grpc.ManagedChannelBuilder; 10 | import io.grpc.StatusRuntimeException; 11 | import utils.ExcludeFromJacocoGeneratedReport; 12 | 13 | import javax.annotation.Nonnull; 14 | import javax.annotation.Nullable; 15 | import java.io.PrintStream; 16 | 17 | public final class BlogClient { 18 | 19 | private BlogClient() {} 20 | 21 | @Nullable 22 | @VisibleForTesting 23 | static BlogId createBlog(BlogServiceGrpc.BlogServiceBlockingStub stub) { 24 | System.out.println("Creating blog...."); 25 | 26 | try { 27 | BlogId createResponse = stub.createBlog( 28 | Blog.newBuilder() 29 | .setAuthor("Clement") 30 | .setTitle("New blog!") 31 | .setContent("Hello world this is my first blog!") 32 | .build() 33 | ); 34 | 35 | System.out.println("Blog created: " + createResponse.getId()); 36 | System.out.println(); 37 | return createResponse; 38 | } catch (StatusRuntimeException e) { 39 | System.out.println("Couldn't create the blog"); 40 | e.printStackTrace(); 41 | return null; 42 | } 43 | } 44 | 45 | @Nullable 46 | @VisibleForTesting 47 | static Blog readBlog(BlogServiceGrpc.BlogServiceBlockingStub stub, @Nonnull BlogId blogId) { 48 | System.out.println("Reading blog...."); 49 | 50 | try { 51 | Blog readResponse = stub.readBlog(blogId); 52 | 53 | System.out.println("Blog read:" + readResponse); 54 | return readResponse; 55 | } catch (StatusRuntimeException e) { 56 | System.out.println("Couldn't read the blog"); 57 | e.printStackTrace(); 58 | return null; 59 | } 60 | } 61 | 62 | @Nullable 63 | @VisibleForTesting 64 | @SuppressWarnings("ResultOfMethodCallIgnored") 65 | static Blog updateBlog(BlogServiceGrpc.BlogServiceBlockingStub stub, @Nonnull BlogId blogId) { 66 | try { 67 | Blog newBlog = Blog.newBuilder() 68 | .setId(blogId.getId()) 69 | .setAuthor("Changed Author") 70 | .setTitle("New blog (updated)!") 71 | .setContent("Hello world this is my first blog! I've added some more content") 72 | .build(); 73 | 74 | System.out.println("Updating blog..."); 75 | stub.updateBlog(newBlog); 76 | 77 | System.out.println("Blog updated:"); 78 | System.out.println(newBlog); 79 | return newBlog; 80 | } catch (StatusRuntimeException e) { 81 | System.out.println("Couldn't update the blog"); 82 | e.printStackTrace(); 83 | return null; 84 | } 85 | } 86 | 87 | @VisibleForTesting 88 | static void listBlogs(BlogServiceGrpc.BlogServiceBlockingStub stub, PrintStream ps) { 89 | ps.println("Listing blogs..."); 90 | stub.listBlogs(Empty.getDefaultInstance()).forEachRemaining(ps::print); 91 | } 92 | 93 | @Nullable 94 | @VisibleForTesting 95 | @SuppressWarnings("ResultOfMethodCallIgnored") 96 | static BlogId deleteBlog(BlogServiceGrpc.BlogServiceBlockingStub stub, @Nonnull BlogId blogId) { 97 | try { 98 | System.out.println("Deleting blog"); 99 | stub.deleteBlog(blogId); 100 | 101 | System.out.println("Blog deleted: " + blogId.getId()); 102 | return blogId; 103 | } catch (StatusRuntimeException e) { 104 | System.out.println("Couldn't delete the blog"); 105 | e.printStackTrace(); 106 | return null; 107 | } 108 | } 109 | 110 | @ExcludeFromJacocoGeneratedReport 111 | private static void run(ManagedChannel channel) { 112 | BlogServiceGrpc.BlogServiceBlockingStub stub = BlogServiceGrpc.newBlockingStub(channel); 113 | 114 | BlogId id = createBlog(stub); 115 | 116 | if (id == null) 117 | return; 118 | 119 | readBlog(stub, id); 120 | updateBlog(stub, id); 121 | listBlogs(stub, System.out); 122 | deleteBlog(stub, id); 123 | } 124 | 125 | @ExcludeFromJacocoGeneratedReport 126 | public static void main(String[] args) { 127 | ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 50051) 128 | .usePlaintext() 129 | .build(); 130 | 131 | run(channel); 132 | 133 | System.out.println("Shutting Down"); 134 | channel.shutdown(); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/main/java/blog/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.1' 2 | 3 | services: 4 | mongo: 5 | image: mongo 6 | restart: always 7 | ports: 8 | - "27017:27017" 9 | environment: 10 | MONGO_INITDB_ROOT_USERNAME: root 11 | MONGO_INITDB_ROOT_PASSWORD: root 12 | 13 | mongo-express: 14 | image: mongo-express 15 | restart: always 16 | ports: 17 | - "8081:8081" 18 | environment: 19 | ME_CONFIG_MONGODB_ADMINUSERNAME: root 20 | ME_CONFIG_MONGODB_ADMINPASSWORD: root 21 | ME_CONFIG_MONGODB_URL: mongodb://root:root@mongo:27017/ -------------------------------------------------------------------------------- /src/main/java/blog/server/BlogServer.java: -------------------------------------------------------------------------------- 1 | package blog.server; 2 | 3 | import com.mongodb.client.MongoClient; 4 | import com.mongodb.client.MongoClients; 5 | import io.grpc.Server; 6 | import io.grpc.ServerBuilder; 7 | 8 | import java.io.IOException; 9 | 10 | public final class BlogServer { 11 | public static void main(String[] args) throws InterruptedException, IOException { 12 | int port = 50051; 13 | 14 | MongoClient client = MongoClients.create("mongodb://root:root@localhost:27017/"); 15 | 16 | Server server = ServerBuilder.forPort(port) 17 | .addService(new BlogServiceImpl(client)) 18 | .build(); 19 | 20 | server.start(); 21 | System.out.println("Server Started"); 22 | System.out.println("Listening on port: " + port); 23 | 24 | Runtime.getRuntime().addShutdownHook(new Thread(() -> { 25 | System.out.println("Received Shutdown Request"); 26 | server.shutdown(); 27 | client.close(); 28 | System.out.println("Server Stopped"); 29 | })); 30 | 31 | server.awaitTermination(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/blog/server/BlogServiceImpl.java: -------------------------------------------------------------------------------- 1 | package blog.server; 2 | 3 | import com.google.common.annotations.VisibleForTesting; 4 | import com.google.protobuf.Empty; 5 | import com.mongodb.MongoException; 6 | import com.mongodb.client.*; 7 | import com.mongodb.client.result.DeleteResult; 8 | import com.mongodb.client.result.InsertOneResult; 9 | import com.proto.blog.Blog; 10 | import com.proto.blog.BlogId; 11 | import com.proto.blog.BlogServiceGrpc; 12 | import io.grpc.Status; 13 | import io.grpc.stub.StreamObserver; 14 | import org.bson.Document; 15 | import org.bson.types.ObjectId; 16 | 17 | import static com.mongodb.client.model.Filters.eq; 18 | import static com.mongodb.client.model.Updates.combine; 19 | import static com.mongodb.client.model.Updates.set; 20 | 21 | public final class BlogServiceImpl extends BlogServiceGrpc.BlogServiceImplBase { 22 | 23 | @VisibleForTesting 24 | static final String BLOG_COULDNT_BE_CREATED = "The blog could not be created"; 25 | @VisibleForTesting 26 | static final String BLOG_COULDNT_BE_DELETED = "The blog could not be deleted"; 27 | @VisibleForTesting 28 | static final String BLOG_WAS_NOT_FOUND = "The blog with the corresponding id was not found"; 29 | @VisibleForTesting 30 | static final String ID_CANNOT_BE_EMPTY = "The blog ID cannot be empty"; 31 | 32 | private final MongoCollection mongoCollection; 33 | 34 | BlogServiceImpl(MongoClient client) { 35 | MongoDatabase db = client.getDatabase("blogdb"); 36 | mongoCollection = db.getCollection("blog"); 37 | } 38 | 39 | private io.grpc.StatusRuntimeException error(Status status, String message) { 40 | return status.withDescription(message).asRuntimeException(); 41 | } 42 | 43 | @SuppressWarnings("SameParameterValue") 44 | private io.grpc.StatusRuntimeException error(Status status, String message, String augmentMessage) { 45 | return status.withDescription(message) 46 | .augmentDescription(augmentMessage) 47 | .asRuntimeException(); 48 | } 49 | 50 | Blog documentToBlog(Document document) { 51 | return Blog.newBuilder() 52 | .setAuthor(document.getString("author")) 53 | .setTitle(document.getString("title")) 54 | .setContent(document.getString("content")) 55 | .setId(document.getObjectId("_id").toString()) 56 | .build(); 57 | } 58 | 59 | @Override 60 | public void createBlog(Blog request, StreamObserver responseObserver) { 61 | System.out.println("Received Create Blog request"); 62 | 63 | Document doc = new Document("author", request.getAuthor()) 64 | .append("title", request.getTitle()) 65 | .append("content", request.getContent()); 66 | 67 | System.out.println("Inserting blog..."); 68 | InsertOneResult result; 69 | 70 | try { 71 | result = mongoCollection.insertOne(doc); 72 | } catch (MongoException e) { 73 | responseObserver.onError(error(Status.INTERNAL, BLOG_COULDNT_BE_CREATED, e.getLocalizedMessage())); 74 | return; 75 | } 76 | 77 | if (!result.wasAcknowledged() || result.getInsertedId() == null) { 78 | responseObserver.onError(error(Status.INTERNAL, BLOG_COULDNT_BE_CREATED)); 79 | return; 80 | } 81 | 82 | String id = result.getInsertedId().asObjectId().getValue().toString(); 83 | System.out.println("Inserted blog: " + id); 84 | 85 | responseObserver.onNext(BlogId.newBuilder().setId(id).build()); 86 | responseObserver.onCompleted(); 87 | } 88 | 89 | @Override 90 | public void readBlog(BlogId request, StreamObserver responseObserver) { 91 | System.out.println("Received Read Blog request"); 92 | 93 | if (request.getId().isEmpty()) { 94 | responseObserver.onError(error(Status.INVALID_ARGUMENT, ID_CANNOT_BE_EMPTY)); 95 | return; 96 | } 97 | 98 | String id = request.getId(); 99 | 100 | System.out.println("Searching for a blog with id: " + id); 101 | Document result = mongoCollection.find(eq("_id", new ObjectId(id))).first(); 102 | 103 | if (result == null) { 104 | System.out.println("Blog not found"); 105 | responseObserver.onError( 106 | error( 107 | Status.NOT_FOUND, 108 | BLOG_WAS_NOT_FOUND, 109 | "BlogId: " + id 110 | ) 111 | ); 112 | return; 113 | } 114 | 115 | System.out.println("Blog found, sending response"); 116 | responseObserver.onNext(documentToBlog(result)); 117 | responseObserver.onCompleted(); 118 | } 119 | 120 | @Override 121 | public void updateBlog(Blog request, StreamObserver responseObserver) { 122 | System.out.println("Received Update Blog request"); 123 | 124 | if (request.getId().isEmpty()) { 125 | responseObserver.onError(error(Status.INVALID_ARGUMENT, ID_CANNOT_BE_EMPTY)); 126 | return; 127 | } 128 | 129 | String id = request.getId(); 130 | 131 | System.out.println("Searching for a blog so we can update it"); 132 | Document result = mongoCollection.findOneAndUpdate( 133 | eq("_id", new ObjectId(id)), 134 | combine( 135 | set("author", request.getAuthor()), 136 | set("title", request.getTitle()), 137 | set("content", request.getContent()) 138 | ) 139 | ); 140 | 141 | if (result == null) { 142 | System.out.println("Blog not found"); 143 | responseObserver.onError( 144 | error( 145 | Status.NOT_FOUND, 146 | BLOG_WAS_NOT_FOUND, 147 | "BlogId: " + id 148 | ) 149 | ); 150 | return; 151 | } 152 | 153 | System.out.println("Replaced! Sending as a response"); 154 | responseObserver.onNext(Empty.getDefaultInstance()); 155 | responseObserver.onCompleted(); 156 | } 157 | 158 | @Override 159 | public void deleteBlog(BlogId request, StreamObserver responseObserver) { 160 | System.out.println("Received Delete Blog Request"); 161 | 162 | if (request.getId().isEmpty()) { 163 | responseObserver.onError(error(Status.INVALID_ARGUMENT, ID_CANNOT_BE_EMPTY)); 164 | return; 165 | } 166 | 167 | String id = request.getId(); 168 | DeleteResult result; 169 | 170 | try { 171 | result = mongoCollection.deleteOne(eq("_id", new ObjectId(id))); 172 | } catch (MongoException e) { 173 | responseObserver.onError(error(Status.INTERNAL, BLOG_COULDNT_BE_DELETED, e.getLocalizedMessage())); 174 | return; 175 | } 176 | 177 | if (!result.wasAcknowledged()) { 178 | System.out.println("Blog could not be deleted"); 179 | responseObserver.onError(error(Status.INTERNAL, BLOG_COULDNT_BE_DELETED)); 180 | return; 181 | } 182 | 183 | if (result.getDeletedCount() == 0) { 184 | System.out.println("Blog not found"); 185 | responseObserver.onError( 186 | error( 187 | Status.NOT_FOUND, 188 | BLOG_WAS_NOT_FOUND, 189 | "BlogId: " + id 190 | ) 191 | ); 192 | return; 193 | } 194 | 195 | System.out.println("Blog was deleted"); 196 | responseObserver.onNext(Empty.getDefaultInstance()); 197 | responseObserver.onCompleted(); 198 | } 199 | 200 | @Override 201 | public void listBlogs(Empty request, StreamObserver responseObserver) { 202 | System.out.println("Received List Blog Request"); 203 | 204 | for (Document document : mongoCollection.find()) { 205 | responseObserver.onNext(documentToBlog(document)); 206 | } 207 | 208 | responseObserver.onCompleted(); 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /src/main/java/calculator/client/CalculatorClient.java: -------------------------------------------------------------------------------- 1 | package calculator.client; 2 | 3 | import com.proto.calculator.*; 4 | import io.grpc.ManagedChannel; 5 | import io.grpc.ManagedChannelBuilder; 6 | import io.grpc.stub.StreamObserver; 7 | 8 | import java.util.Arrays; 9 | import java.util.concurrent.CountDownLatch; 10 | import java.util.concurrent.TimeUnit; 11 | import java.util.stream.IntStream; 12 | 13 | public final class CalculatorClient { 14 | private static void doSum(ManagedChannel channel) { 15 | System.out.println("Enter doSum"); 16 | CalculatorServiceGrpc.CalculatorServiceBlockingStub stub = CalculatorServiceGrpc.newBlockingStub(channel); 17 | SumResponse response = stub.sum(SumRequest.newBuilder().setFirstNumber(1).setSecondNumber(1).build()); 18 | 19 | System.out.println("Sum 1 + 1 = " + response.getResult()); 20 | } 21 | 22 | private static void doPrimes(ManagedChannel channel) { 23 | System.out.println("Enter doPrimes"); 24 | CalculatorServiceGrpc.CalculatorServiceBlockingStub stub = CalculatorServiceGrpc.newBlockingStub(channel); 25 | 26 | stub.primes(PrimeRequest.newBuilder().setNumber(567890).build()).forEachRemaining(response -> 27 | System.out.println(response.getPrimeFactor()) 28 | ); 29 | } 30 | 31 | private static void doAvg(ManagedChannel channel) throws InterruptedException { 32 | System.out.println("Enter doAvg"); 33 | CalculatorServiceGrpc.CalculatorServiceStub stub = CalculatorServiceGrpc.newStub(channel); 34 | CountDownLatch latch = new CountDownLatch(1); 35 | 36 | StreamObserver stream = stub.avg(new StreamObserver() { 37 | @Override 38 | public void onNext(AvgResponse response) { 39 | System.out.println("Avg = " + response.getResult()); 40 | } 41 | 42 | @Override 43 | public void onError(Throwable t) {} 44 | 45 | @Override 46 | public void onCompleted() { 47 | latch.countDown(); 48 | } 49 | }); 50 | 51 | IntStream.range(1, 11).forEach(number -> 52 | stream.onNext(AvgRequest.newBuilder().setNumber(number).build()) 53 | ); 54 | 55 | stream.onCompleted(); 56 | latch.await(); 57 | } 58 | 59 | private static void doMax(ManagedChannel channel) throws InterruptedException { 60 | System.out.println("Enter doMax"); 61 | CalculatorServiceGrpc.CalculatorServiceStub stub = CalculatorServiceGrpc.newStub(channel); 62 | CountDownLatch latch = new CountDownLatch(1); 63 | 64 | StreamObserver stream = stub.max(new StreamObserver() { 65 | @Override 66 | public void onNext(MaxResponse response) { 67 | System.out.println("Max = " + response.getMax()); 68 | } 69 | 70 | @Override 71 | public void onError(Throwable t) {} 72 | 73 | @Override 74 | public void onCompleted() { 75 | latch.countDown(); 76 | } 77 | }); 78 | 79 | Arrays.asList(1, 5, 3, 6, 2, 20).forEach(number -> 80 | stream.onNext(MaxRequest.newBuilder().setNumber(number).build()) 81 | ); 82 | 83 | stream.onCompleted(); 84 | 85 | //noinspection ResultOfMethodCallIgnored 86 | latch.await(3, TimeUnit.SECONDS); 87 | } 88 | 89 | private static void doSqrt(CalculatorServiceGrpc.CalculatorServiceBlockingStub stub) { 90 | System.out.println("Enter doSqrt"); 91 | SqrtResponse response = stub.sqrt(SqrtRequest.newBuilder().setNumber(25).build()); 92 | 93 | System.out.println("Sqrt 25 = " + response.getResult()); 94 | 95 | try { 96 | response = stub.sqrt(SqrtRequest.newBuilder().setNumber(-1).build()); 97 | System.out.println("Sqrt -1 = " + response.getResult()); 98 | } catch (RuntimeException e) { 99 | System.out.println("Got an exception for sqrt"); 100 | e.printStackTrace(); 101 | } 102 | } 103 | 104 | public static void main(String[] args) throws InterruptedException { 105 | if (args.length == 0) { 106 | System.out.println("Need one argument to work"); 107 | return; 108 | } 109 | 110 | ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 50052) 111 | .usePlaintext() 112 | .build(); 113 | 114 | switch (args[0]) { 115 | case "sum": doSum(channel); break; 116 | case "primes": doPrimes(channel); break; 117 | case "avg": doAvg(channel); break; 118 | case "max": doMax(channel); break; 119 | case "sqrt": { 120 | CalculatorServiceGrpc.CalculatorServiceBlockingStub stub = CalculatorServiceGrpc.newBlockingStub(channel); 121 | doSqrt(stub); 122 | break; 123 | } 124 | default: System.out.println("Keyword Invalid: " + args[0]); 125 | } 126 | 127 | System.out.println("Shutting Down"); 128 | channel.shutdown(); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/main/java/calculator/server/CalculatorServer.java: -------------------------------------------------------------------------------- 1 | package calculator.server; 2 | 3 | import io.grpc.Server; 4 | import io.grpc.ServerBuilder; 5 | 6 | import java.io.IOException; 7 | 8 | public final class CalculatorServer { 9 | public static void main(String[] args) throws IOException, InterruptedException { 10 | int port = 50052; 11 | 12 | Server server = ServerBuilder.forPort(port) 13 | .addService(new CalculatorServiceImpl()) 14 | .build(); 15 | 16 | server.start(); 17 | System.out.println("Server Started"); 18 | System.out.println("Listening on port: " + port); 19 | 20 | Runtime.getRuntime().addShutdownHook(new Thread(() -> { 21 | System.out.println("Received Shutdown Request"); 22 | server.shutdown(); 23 | System.out.println("Server Stopped"); 24 | })); 25 | 26 | server.awaitTermination(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/calculator/server/CalculatorServiceImpl.java: -------------------------------------------------------------------------------- 1 | package calculator.server; 2 | 3 | import com.proto.calculator.*; 4 | import io.grpc.Status; 5 | import io.grpc.stub.StreamObserver; 6 | 7 | public final class CalculatorServiceImpl extends CalculatorServiceGrpc.CalculatorServiceImplBase { 8 | @Override 9 | public void sum(SumRequest request, StreamObserver responseObserver) { 10 | responseObserver.onNext(SumResponse.newBuilder().setResult( 11 | request.getFirstNumber() + request.getSecondNumber() 12 | ).build()); 13 | responseObserver.onCompleted(); 14 | } 15 | 16 | @Override 17 | public void primes(PrimeRequest request, StreamObserver responseObserver) { 18 | int number = request.getNumber(); 19 | int divisor = 2; 20 | 21 | while (number > 1) { 22 | if (number % divisor == 0) { 23 | number /= divisor; 24 | responseObserver.onNext(PrimeResponse.newBuilder().setPrimeFactor(divisor).build()); 25 | } else { 26 | ++divisor; 27 | } 28 | } 29 | 30 | responseObserver.onCompleted(); 31 | } 32 | 33 | @Override 34 | public StreamObserver avg(StreamObserver responseObserver) { 35 | return new StreamObserver() { 36 | int sum = 0; 37 | int count = 0; 38 | 39 | @Override 40 | public void onNext(AvgRequest value) { 41 | sum += value.getNumber(); 42 | ++count; 43 | } 44 | 45 | @Override 46 | public void onError(Throwable t) { 47 | responseObserver.onError(t); 48 | } 49 | 50 | @Override 51 | public void onCompleted() { 52 | responseObserver.onNext(AvgResponse.newBuilder().setResult( 53 | (double) sum / count 54 | ).build()); 55 | responseObserver.onCompleted(); 56 | } 57 | }; 58 | } 59 | 60 | @Override 61 | public StreamObserver max(StreamObserver responseObserver) { 62 | return new StreamObserver() { 63 | int max = 0; 64 | 65 | @Override 66 | public void onNext(MaxRequest value) { 67 | if (value.getNumber() > max) { 68 | max = value.getNumber(); 69 | responseObserver.onNext(MaxResponse.newBuilder().setMax(max).build()); 70 | } 71 | } 72 | 73 | @Override 74 | public void onError(Throwable t) { 75 | responseObserver.onError(t); 76 | } 77 | 78 | @Override 79 | public void onCompleted() { 80 | responseObserver.onCompleted(); 81 | } 82 | }; 83 | } 84 | 85 | @Override 86 | public void sqrt(SqrtRequest request, StreamObserver responseObserver) { 87 | int number = request.getNumber(); 88 | 89 | if (number < 0) { 90 | responseObserver.onError(Status.INVALID_ARGUMENT 91 | .withDescription("The number being sent cannot be negative") 92 | .augmentDescription("Number: " + number) 93 | .asRuntimeException()); 94 | return; 95 | } 96 | 97 | responseObserver.onNext( 98 | SqrtResponse.newBuilder().setResult(Math.sqrt(number)).build() 99 | ); 100 | responseObserver.onCompleted(); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/greeting/client/AddHeaderInterceptor.java: -------------------------------------------------------------------------------- 1 | package greeting.client; 2 | 3 | import io.grpc.*; 4 | 5 | import static greeting.server.HeaderCheckInterceptor.CUSTOM_HEADER_KEY; 6 | 7 | public final class AddHeaderInterceptor implements ClientInterceptor { 8 | @Override 9 | public ClientCall interceptCall(MethodDescriptor method, CallOptions callOptions, Channel next) { 10 | return new ForwardingClientCall.SimpleForwardingClientCall(next.newCall(method, callOptions)) { 11 | @Override 12 | public void start(Listener responseListener, Metadata headers) { 13 | headers.put(CUSTOM_HEADER_KEY, "customRequestValue"); 14 | super.start(responseListener, headers); 15 | } 16 | }; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/greeting/client/GreetingClient.java: -------------------------------------------------------------------------------- 1 | package greeting.client; 2 | 3 | import com.proto.greeting.*; 4 | import io.grpc.*; 5 | import io.grpc.stub.StreamObserver; 6 | 7 | import java.util.Arrays; 8 | import java.util.concurrent.CountDownLatch; 9 | import java.util.concurrent.TimeUnit; 10 | 11 | public final class GreetingClient { 12 | private static void doGreet(ManagedChannel channel) { 13 | System.out.println("Enter doGreet"); 14 | GreetingServiceGrpc.GreetingServiceBlockingStub stub = GreetingServiceGrpc.newBlockingStub(channel); 15 | GreetingResponse response = stub.greet(GreetingRequest.newBuilder().setFirstName("Clement").build()); 16 | 17 | System.out.println("Greeting: " + response.getResult()); 18 | } 19 | 20 | private static void doGreetManyTimes(ManagedChannel channel) { 21 | System.out.println("Enter doGreetManyTimes"); 22 | GreetingServiceGrpc.GreetingServiceBlockingStub stub = GreetingServiceGrpc.newBlockingStub(channel); 23 | 24 | stub.greetManyTimes(GreetingRequest.newBuilder().setFirstName("Clement").build()).forEachRemaining(response -> 25 | System.out.println(response.getResult()) 26 | ); 27 | } 28 | 29 | private static void doLongGreet(ManagedChannel channel) throws InterruptedException { 30 | System.out.println("Enter doLongGreet"); 31 | GreetingServiceGrpc.GreetingServiceStub stub = GreetingServiceGrpc.newStub(channel); 32 | CountDownLatch latch = new CountDownLatch(1); 33 | 34 | StreamObserver stream = stub.longGreet(new StreamObserver() { 35 | @Override 36 | public void onNext(GreetingResponse response) { 37 | System.out.println(response.getResult()); 38 | } 39 | 40 | @Override 41 | public void onError(Throwable t) {} 42 | 43 | @Override 44 | public void onCompleted() { 45 | latch.countDown(); 46 | } 47 | }); 48 | 49 | Arrays.asList("Clement", "Marie", "Test").forEach(name -> 50 | stream.onNext(GreetingRequest.newBuilder().setFirstName(name).build()) 51 | ); 52 | 53 | stream.onCompleted(); 54 | 55 | //noinspection ResultOfMethodCallIgnored 56 | latch.await(3, TimeUnit.SECONDS); 57 | } 58 | 59 | private static void doGreetEveryone(ManagedChannel channel) throws InterruptedException { 60 | System.out.println("Enter doGreetEveryone"); 61 | GreetingServiceGrpc.GreetingServiceStub stub = GreetingServiceGrpc.newStub(channel); 62 | CountDownLatch latch = new CountDownLatch(1); 63 | 64 | StreamObserver stream = stub.greetEveryone(new StreamObserver() { 65 | @Override 66 | public void onNext(GreetingResponse response) { 67 | System.out.println(response.getResult()); 68 | } 69 | 70 | @Override 71 | public void onError(Throwable t) {} 72 | 73 | @Override 74 | public void onCompleted() { 75 | latch.countDown(); 76 | } 77 | }); 78 | 79 | Arrays.asList("Clement", "Marie", "Test").forEach(name -> 80 | stream.onNext(GreetingRequest.newBuilder().setFirstName(name).build()) 81 | ); 82 | 83 | stream.onCompleted(); 84 | 85 | //noinspection ResultOfMethodCallIgnored 86 | latch.await(3, TimeUnit.SECONDS); 87 | } 88 | 89 | private static void doGreetWithDeadline(ManagedChannel channel) { 90 | System.out.println("Enter doGreetWithDeadline"); 91 | GreetingServiceGrpc.GreetingServiceBlockingStub stub = GreetingServiceGrpc.newBlockingStub(channel); 92 | GreetingRequest request = GreetingRequest.newBuilder().setFirstName("Clement").build(); 93 | GreetingResponse response = stub.withDeadline(Deadline.after(3, TimeUnit.SECONDS)) 94 | .greetWithDeadline(request); 95 | 96 | System.out.println("Greeting within deadline: " + response.getResult()); 97 | 98 | try { 99 | response = stub.withDeadline(Deadline.after(100, TimeUnit.MILLISECONDS)) 100 | .greetWithDeadline(request); 101 | 102 | System.out.println("Greeting deadline exceeded: " + response.getResult()); 103 | } catch (StatusRuntimeException e) { 104 | if (e.getStatus().getCode() == Status.Code.DEADLINE_EXCEEDED) { 105 | System.out.println("Deadline has been exceeded"); 106 | } else { 107 | System.out.println("Got an exception in greetWithDeadline"); 108 | e.printStackTrace(); 109 | } 110 | } 111 | } 112 | 113 | public static void main(String[] args) throws InterruptedException { 114 | if (args.length == 0) { 115 | System.out.println("Need one argument to work"); 116 | return; 117 | } 118 | 119 | ManagedChannel channel = ManagedChannelBuilder 120 | .forAddress("localhost", 50051) 121 | .intercept(new LogInterceptor()) 122 | .intercept(new AddHeaderInterceptor()) 123 | .usePlaintext() 124 | .build(); 125 | 126 | switch (args[0]) { 127 | case "greet": doGreet(channel); break; 128 | case "greet_many_times": doGreetManyTimes(channel); break; 129 | case "greet_long": doLongGreet(channel); break; 130 | case "greet_everyone": doGreetEveryone(channel); break; 131 | case "greet_with_deadline": doGreetWithDeadline(channel); break; 132 | default: System.out.println("Keyword Invalid: " + args[0]); 133 | } 134 | 135 | System.out.println("Shutting Down"); 136 | channel.shutdown(); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/main/java/greeting/client/GreetingClientTls.java: -------------------------------------------------------------------------------- 1 | package greeting.client; 2 | 3 | import com.proto.greeting.GreetingRequest; 4 | import com.proto.greeting.GreetingResponse; 5 | import com.proto.greeting.GreetingServiceGrpc; 6 | import io.grpc.*; 7 | 8 | import java.io.File; 9 | import java.io.IOException; 10 | 11 | public final class GreetingClientTls { 12 | private static void doGreet(ManagedChannel channel) { 13 | System.out.println("Enter doGreet"); 14 | GreetingServiceGrpc.GreetingServiceBlockingStub stub = GreetingServiceGrpc.newBlockingStub(channel); 15 | GreetingResponse response = stub.greet(GreetingRequest.newBuilder().setFirstName("Clement").build()); 16 | 17 | System.out.println("Greeting: " + response.getResult()); 18 | } 19 | 20 | public static void main(String[] args) throws IOException { 21 | ChannelCredentials creds = TlsChannelCredentials.newBuilder().trustManager( 22 | new File("ssl/ca.crt") 23 | ).build(); 24 | ManagedChannel channel = Grpc.newChannelBuilderForAddress("localhost", 50051, creds).build(); 25 | 26 | doGreet(channel); 27 | 28 | System.out.println("Shutting Down"); 29 | channel.shutdown(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/greeting/client/LogInterceptor.java: -------------------------------------------------------------------------------- 1 | package greeting.client; 2 | 3 | import io.grpc.*; 4 | 5 | public final class LogInterceptor implements ClientInterceptor { 6 | @Override 7 | public ClientCall interceptCall(MethodDescriptor method, CallOptions callOptions, Channel next) { 8 | return new ForwardingClientCall.SimpleForwardingClientCall(next.newCall(method, callOptions)) { 9 | @Override 10 | public void sendMessage(ReqT message) { 11 | System.out.println("Send a message"); 12 | System.out.println(message); 13 | 14 | System.out.println("With call options"); 15 | System.out.println(callOptions.toString()); 16 | super.sendMessage(message); 17 | } 18 | }; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/greeting/server/GreetingServer.java: -------------------------------------------------------------------------------- 1 | package greeting.server; 2 | 3 | import io.grpc.Server; 4 | import io.grpc.ServerBuilder; 5 | import io.grpc.protobuf.services.ProtoReflectionService; 6 | 7 | import java.io.IOException; 8 | 9 | public final class GreetingServer { 10 | public static void main(String[] args) throws IOException, InterruptedException { 11 | int port = 50051; 12 | 13 | Server server = ServerBuilder.forPort(port) 14 | .addService(new GreetingServiceImpl(new SleeperImpl())) 15 | .addService(ProtoReflectionService.newInstance()) 16 | .intercept(new LogInterceptor()) 17 | .intercept(new HeaderCheckInterceptor()) 18 | .build(); 19 | 20 | server.start(); 21 | System.out.println("Server Started"); 22 | System.out.println("Listening on port: " + port); 23 | 24 | Runtime.getRuntime().addShutdownHook(new Thread(() -> { 25 | System.out.println("Received Shutdown Request"); 26 | server.shutdown(); 27 | System.out.println("Server Stopped"); 28 | })); 29 | 30 | server.awaitTermination(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/greeting/server/GreetingServerTls.java: -------------------------------------------------------------------------------- 1 | package greeting.server; 2 | 3 | import io.grpc.Server; 4 | import io.grpc.ServerBuilder; 5 | 6 | import java.io.File; 7 | import java.io.IOException; 8 | 9 | public final class GreetingServerTls { 10 | public static void main(String[] args) throws IOException, InterruptedException { 11 | int port = 50051; 12 | 13 | Server server = ServerBuilder.forPort(port) 14 | .useTransportSecurity( 15 | new File("ssl/server.crt"), 16 | new File("ssl/server.pem") 17 | ) 18 | .addService(new GreetingServiceImpl(new SleeperImpl())) 19 | .build(); 20 | 21 | server.start(); 22 | System.out.println("Server Started"); 23 | System.out.println("Listening on port: " + port); 24 | 25 | Runtime.getRuntime().addShutdownHook(new Thread(() -> { 26 | System.out.println("Received Shutdown Request"); 27 | server.shutdown(); 28 | System.out.println("Server Stopped"); 29 | })); 30 | 31 | server.awaitTermination(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/greeting/server/GreetingServiceImpl.java: -------------------------------------------------------------------------------- 1 | package greeting.server; 2 | 3 | import com.proto.greeting.GreetingRequest; 4 | import com.proto.greeting.GreetingResponse; 5 | import com.proto.greeting.GreetingServiceGrpc; 6 | import io.grpc.Context; 7 | import io.grpc.stub.StreamObserver; 8 | import utils.Sleeper; 9 | 10 | public final class GreetingServiceImpl extends GreetingServiceGrpc.GreetingServiceImplBase { 11 | 12 | final Sleeper sleeper; 13 | 14 | GreetingServiceImpl(Sleeper sleeper) { 15 | this.sleeper = sleeper; 16 | } 17 | 18 | @Override 19 | public void greet(GreetingRequest request, StreamObserver responseObserver) { 20 | responseObserver.onNext(GreetingResponse.newBuilder().setResult("Hello " + request.getFirstName()).build()); 21 | responseObserver.onCompleted(); 22 | } 23 | 24 | @Override 25 | public void greetManyTimes(GreetingRequest request, StreamObserver responseObserver) { 26 | GreetingResponse response = GreetingResponse.newBuilder().setResult("Hello " + request.getFirstName()).build(); 27 | 28 | for (int i = 0; i < 10; ++i) { 29 | responseObserver.onNext(response); 30 | } 31 | 32 | responseObserver.onCompleted(); 33 | } 34 | 35 | @Override 36 | public StreamObserver longGreet(StreamObserver responseObserver) { 37 | StringBuilder sb = new StringBuilder(); 38 | 39 | return new StreamObserver() { 40 | @Override 41 | public void onNext(GreetingRequest request) { 42 | sb.append("Hello ") 43 | .append(request.getFirstName()) 44 | .append("!\n"); 45 | } 46 | 47 | @Override 48 | public void onError(Throwable t) { 49 | responseObserver.onError(t); 50 | } 51 | 52 | @Override 53 | public void onCompleted() { 54 | responseObserver.onNext(GreetingResponse.newBuilder().setResult(sb.toString()).build()); 55 | responseObserver.onCompleted(); 56 | } 57 | }; 58 | } 59 | 60 | @Override 61 | public StreamObserver greetEveryone(StreamObserver responseObserver) { 62 | return new StreamObserver() { 63 | @Override 64 | public void onNext(GreetingRequest request) { 65 | responseObserver.onNext(GreetingResponse.newBuilder().setResult("Hello " + request.getFirstName()).build()); 66 | } 67 | 68 | @Override 69 | public void onError(Throwable t) { 70 | responseObserver.onError(t); 71 | } 72 | 73 | @Override 74 | public void onCompleted() { 75 | responseObserver.onCompleted(); 76 | } 77 | }; 78 | } 79 | 80 | @Override 81 | public void greetWithDeadline(GreetingRequest request, StreamObserver responseObserver) { 82 | Context current = Context.current(); 83 | 84 | try { 85 | for (int i = 0; i < 3; ++i) { 86 | if (current.isCancelled()) 87 | return; 88 | 89 | sleeper.sleep(100); 90 | } 91 | 92 | responseObserver.onNext(GreetingResponse.newBuilder().setResult("Hello " + request.getFirstName()).build()); 93 | responseObserver.onCompleted(); 94 | } catch (InterruptedException e) { 95 | responseObserver.onError(e); 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/greeting/server/HeaderCheckInterceptor.java: -------------------------------------------------------------------------------- 1 | package greeting.server; 2 | 3 | import com.google.common.annotations.VisibleForTesting; 4 | import io.grpc.*; 5 | 6 | public final class HeaderCheckInterceptor implements ServerInterceptor { 7 | 8 | @VisibleForTesting 9 | public static final Metadata.Key CUSTOM_HEADER_KEY = 10 | Metadata.Key.of("custom_server_header_key", Metadata.ASCII_STRING_MARSHALLER); 11 | 12 | @Override 13 | public ServerCall.Listener interceptCall(ServerCall call, Metadata headers, ServerCallHandler next) { 14 | if (!headers.containsKey(CUSTOM_HEADER_KEY)) { 15 | call.close(Status.CANCELLED, new Metadata()); 16 | return new ServerCall.Listener() {}; 17 | } 18 | 19 | return new ForwardingServerCallListener.SimpleForwardingServerCallListener(next.startCall(call, headers)) {}; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/greeting/server/LogInterceptor.java: -------------------------------------------------------------------------------- 1 | package greeting.server; 2 | 3 | import io.grpc.*; 4 | 5 | public final class LogInterceptor implements ServerInterceptor { 6 | @Override 7 | public ServerCall.Listener interceptCall(ServerCall call, Metadata headers, ServerCallHandler next) { 8 | return new ForwardingServerCallListener.SimpleForwardingServerCallListener(next.startCall(call, headers)) { 9 | @Override 10 | public void onMessage(ReqT message) { 11 | System.out.println("Receive a message"); 12 | System.out.println(message); 13 | 14 | System.out.println("With headers"); 15 | System.out.println(headers); 16 | super.onMessage(message); 17 | } 18 | }; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/greeting/server/SleeperImpl.java: -------------------------------------------------------------------------------- 1 | package greeting.server; 2 | 3 | import utils.Sleeper; 4 | 5 | public class SleeperImpl implements Sleeper { 6 | 7 | @Override 8 | public void sleep(long millis) throws InterruptedException { 9 | Thread.sleep(millis); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/utils/ExcludeFromJacocoGeneratedReport.java: -------------------------------------------------------------------------------- 1 | package utils; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target({ElementType.METHOD, ElementType.CONSTRUCTOR}) 10 | public @interface ExcludeFromJacocoGeneratedReport {} 11 | -------------------------------------------------------------------------------- /src/main/java/utils/Sleeper.java: -------------------------------------------------------------------------------- 1 | package utils; 2 | 3 | public interface Sleeper { 4 | void sleep(long millis) throws InterruptedException; 5 | } 6 | -------------------------------------------------------------------------------- /src/main/proto/blog/blog.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package blog; 4 | 5 | import "google/protobuf/empty.proto"; 6 | 7 | option java_package = "com.proto.blog"; 8 | option java_multiple_files = true; 9 | 10 | message Blog { 11 | string id = 1; 12 | string author = 2; 13 | string title = 3; 14 | string content = 4; 15 | } 16 | 17 | message BlogId { 18 | string id = 1; 19 | } 20 | 21 | service BlogService { 22 | // Requests the creation for a Blog 23 | // Returns Status.INTERNAL if the blog couldn't be created due to Db error 24 | // Returns the created Blog's Id 25 | rpc CreateBlog(Blog) returns (BlogId); 26 | 27 | // Requests access to the content of a Blog by sending an Id 28 | // Returns Status.NOT_FOUND if the Id doesn't match any Blog in Db 29 | // Returns Blog content 30 | rpc ReadBlog(BlogId) returns (Blog); 31 | 32 | // Requests the update of a Blog in Db 33 | // Returns Status.NOT_FOUND if the Id doesn't match any Blog in Db 34 | // Returns Status.INTERNAL if the blog couldn't be updated due to Db error 35 | // Returns Empty 36 | rpc UpdateBlog(Blog) returns (google.protobuf.Empty); 37 | 38 | // Requests the delete of a Blog in Db by giving its Id 39 | // Returns Status.NOT_FOUND if the Id doesn't match any Blog in Db 40 | // Returns Status.INTERNAL if the blog couldn't be deleted due to Db error 41 | // Returns Empty 42 | rpc DeleteBlog(BlogId) returns (google.protobuf.Empty); 43 | 44 | // Requests access to all the Blogs in Db 45 | // Returns stream of Blogs 46 | rpc ListBlogs(google.protobuf.Empty) returns (stream Blog); 47 | } -------------------------------------------------------------------------------- /src/main/proto/calculator/avg.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package calculator; 4 | 5 | option java_package = "com.proto.calculator"; 6 | option java_multiple_files = true; 7 | 8 | message AvgRequest { 9 | int32 number = 1; 10 | } 11 | 12 | message AvgResponse { 13 | double result = 1; 14 | } -------------------------------------------------------------------------------- /src/main/proto/calculator/calculator.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package calculator; 4 | 5 | import "calculator/avg.proto"; 6 | import "calculator/max.proto"; 7 | import "calculator/prime.proto"; 8 | import "calculator/sqrt.proto"; 9 | import "calculator/sum.proto"; 10 | 11 | option java_package = "com.proto.calculator"; 12 | option java_multiple_files = true; 13 | 14 | service CalculatorService { 15 | rpc Sum(SumRequest) returns (SumResponse); 16 | rpc Primes(PrimeRequest) returns (stream PrimeResponse); 17 | rpc Avg(stream AvgRequest) returns (AvgResponse); 18 | rpc Max(stream MaxRequest) returns (stream MaxResponse); 19 | 20 | // Returns a Status.INVALID_ARGUMENT if the SqrtRequest.number is negative 21 | rpc Sqrt(SqrtRequest) returns (SqrtResponse); 22 | } -------------------------------------------------------------------------------- /src/main/proto/calculator/max.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package calculator; 4 | 5 | option java_package = "com.proto.calculator"; 6 | option java_multiple_files = true; 7 | 8 | message MaxRequest { 9 | int32 number = 1; 10 | } 11 | 12 | message MaxResponse { 13 | int32 max = 1; 14 | } -------------------------------------------------------------------------------- /src/main/proto/calculator/prime.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package calculator; 4 | 5 | option java_package = "com.proto.calculator"; 6 | option java_multiple_files = true; 7 | 8 | message PrimeRequest { 9 | int32 number = 1; 10 | } 11 | 12 | message PrimeResponse { 13 | int32 prime_factor = 1; 14 | } -------------------------------------------------------------------------------- /src/main/proto/calculator/sqrt.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package calculator; 4 | 5 | option java_package = "com.proto.calculator"; 6 | option java_multiple_files = true; 7 | 8 | message SqrtRequest { 9 | int32 number = 1; 10 | } 11 | 12 | message SqrtResponse { 13 | double result = 1; 14 | } -------------------------------------------------------------------------------- /src/main/proto/calculator/sum.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package calculator; 4 | 5 | option java_package = "com.proto.calculator"; 6 | option java_multiple_files = true; 7 | 8 | message SumRequest { 9 | int32 first_number = 1; 10 | int32 second_number = 2; 11 | }; 12 | 13 | message SumResponse { 14 | int32 result = 1; 15 | } -------------------------------------------------------------------------------- /src/main/proto/dummy.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package dummy; 4 | 5 | option java_package = "com.proto.dummy"; 6 | option java_multiple_files = true; 7 | 8 | message DummyMessage { 9 | 10 | } 11 | 12 | service DummyService { 13 | 14 | } -------------------------------------------------------------------------------- /src/main/proto/greeting/greeting.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package greeting; 4 | 5 | option java_package = "com.proto.greeting"; 6 | option java_multiple_files = true; 7 | 8 | message GreetingRequest { 9 | string first_name = 1; 10 | } 11 | 12 | message GreetingResponse { 13 | string result = 1; 14 | } 15 | 16 | service GreetingService { 17 | rpc Greet(GreetingRequest) returns (GreetingResponse); 18 | rpc GreetManyTimes(GreetingRequest) returns (stream GreetingResponse); 19 | rpc LongGreet(stream GreetingRequest) returns (GreetingResponse); 20 | rpc GreetEveryone(stream GreetingRequest) returns (stream GreetingResponse); 21 | rpc GreetWithDeadline(GreetingRequest) returns (GreetingResponse); 22 | } -------------------------------------------------------------------------------- /src/test/java/blog/client/BlogCreateTest.java: -------------------------------------------------------------------------------- 1 | package blog.client; 2 | 3 | import com.proto.blog.Blog; 4 | import com.proto.blog.BlogId; 5 | import com.proto.blog.BlogServiceGrpc; 6 | import io.grpc.Status; 7 | import io.grpc.stub.StreamObserver; 8 | import org.junit.jupiter.api.Test; 9 | import utils.ClientTestBase; 10 | 11 | import java.io.IOException; 12 | 13 | import static org.junit.jupiter.api.Assertions.assertNotNull; 14 | import static org.junit.jupiter.api.Assertions.assertNull; 15 | 16 | public class BlogCreateTest extends ClientTestBase { 17 | private static class CreateSuccessBlogServer extends BlogServiceGrpc.BlogServiceImplBase { 18 | @Override 19 | public void createBlog(Blog request, StreamObserver responseObserver) { 20 | responseObserver.onNext(BlogId.newBuilder().setId("this_is_an_id").build()); 21 | responseObserver.onCompleted(); 22 | } 23 | } 24 | 25 | private static class CreateFailBlogServer extends BlogServiceGrpc.BlogServiceImplBase { 26 | @Override 27 | public void createBlog(Blog request, StreamObserver responseObserver) { 28 | responseObserver.onError(Status.INTERNAL.asRuntimeException()); 29 | } 30 | } 31 | 32 | @Test 33 | void createTest() throws IOException { 34 | createServerWithService(new CreateSuccessBlogServer(), BlogServiceGrpc::newBlockingStub); 35 | 36 | assertNotNull(BlogClient.createBlog(getStub())); 37 | } 38 | 39 | @Test 40 | void createFailTest() throws IOException { 41 | createServerWithService(new CreateFailBlogServer(), BlogServiceGrpc::newBlockingStub); 42 | 43 | assertNull(BlogClient.createBlog(getStub())); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/blog/client/BlogDeleteTest.java: -------------------------------------------------------------------------------- 1 | package blog.client; 2 | 3 | import com.google.protobuf.Empty; 4 | import com.proto.blog.BlogId; 5 | import com.proto.blog.BlogServiceGrpc; 6 | import io.grpc.Status; 7 | import io.grpc.stub.StreamObserver; 8 | import org.junit.jupiter.api.Test; 9 | import utils.ClientTestBase; 10 | 11 | import java.io.IOException; 12 | 13 | import static org.junit.jupiter.api.Assertions.assertNotNull; 14 | import static org.junit.jupiter.api.Assertions.assertNull; 15 | 16 | public class BlogDeleteTest extends ClientTestBase { 17 | 18 | private static class DeleteSuccessBlogServer extends BlogServiceGrpc.BlogServiceImplBase { 19 | @Override 20 | public void deleteBlog(BlogId request, StreamObserver responseObserver) { 21 | responseObserver.onNext(Empty.getDefaultInstance()); 22 | responseObserver.onCompleted(); 23 | } 24 | } 25 | 26 | private static class DeleteFailBlogServer extends BlogServiceGrpc.BlogServiceImplBase { 27 | @Override 28 | public void deleteBlog(BlogId request, StreamObserver responseObserver) { 29 | responseObserver.onError(Status.NOT_FOUND.asRuntimeException()); 30 | } 31 | } 32 | 33 | private final BlogId fakeBlogId = BlogId.newBuilder().setId("this_is_an_id").build(); 34 | 35 | @Test 36 | void deleteTest() throws IOException { 37 | createServerWithService(new DeleteSuccessBlogServer(), BlogServiceGrpc::newBlockingStub); 38 | 39 | assertNotNull(BlogClient.deleteBlog(getStub(), fakeBlogId)); 40 | } 41 | 42 | @Test 43 | void deleteFailTest() throws IOException { 44 | createServerWithService(new DeleteFailBlogServer(), BlogServiceGrpc::newBlockingStub); 45 | 46 | assertNull(BlogClient.deleteBlog(getStub(), fakeBlogId)); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/blog/client/BlogListTest.java: -------------------------------------------------------------------------------- 1 | package blog.client; 2 | 3 | import com.google.protobuf.Empty; 4 | import com.proto.blog.Blog; 5 | import com.proto.blog.BlogServiceGrpc; 6 | import io.grpc.stub.StreamObserver; 7 | import org.junit.jupiter.api.Test; 8 | import utils.ClientTestBase; 9 | 10 | import java.io.ByteArrayOutputStream; 11 | import java.io.IOException; 12 | import java.io.PrintStream; 13 | 14 | import static org.junit.jupiter.api.Assertions.assertEquals; 15 | 16 | public class BlogListTest extends ClientTestBase { 17 | 18 | private static class ListEmptyBlogServer extends BlogServiceGrpc.BlogServiceImplBase { 19 | @Override 20 | public void listBlogs(Empty request, StreamObserver responseObserver) { 21 | responseObserver.onCompleted(); 22 | } 23 | } 24 | 25 | private static class ListNonEmptyBlogServer extends BlogServiceGrpc.BlogServiceImplBase { 26 | @Override 27 | public void listBlogs(Empty request, StreamObserver responseObserver) { 28 | responseObserver.onNext(Blog.newBuilder() 29 | .setId("id") 30 | .setAuthor("Clement") 31 | .setTitle("Title") 32 | .setContent("Content") 33 | .build() 34 | ); 35 | responseObserver.onCompleted(); 36 | } 37 | } 38 | 39 | @Test 40 | void listEmptyTest() throws IOException { 41 | createServerWithService(new ListEmptyBlogServer(), BlogServiceGrpc::newBlockingStub); 42 | ByteArrayOutputStream outSpy = new ByteArrayOutputStream(); 43 | PrintStream ps = new PrintStream(outSpy); 44 | 45 | BlogClient.listBlogs(getStub(), ps); 46 | assertEquals("Listing blogs...\n", outSpy.toString()); 47 | } 48 | 49 | @Test 50 | void listNonEmptyTest() throws IOException { 51 | createServerWithService(new ListNonEmptyBlogServer(), BlogServiceGrpc::newBlockingStub); 52 | ByteArrayOutputStream outSpy = new ByteArrayOutputStream(); 53 | PrintStream ps = new PrintStream(outSpy); 54 | 55 | BlogClient.listBlogs(getStub(), ps); 56 | assertEquals( 57 | "Listing blogs...\n" + 58 | "id: \"id\"\n" + 59 | "author: \"Clement\"\n" + 60 | "title: \"Title\"\n" + 61 | "content: \"Content\"\n", 62 | outSpy.toString() 63 | ); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/test/java/blog/client/BlogReadTest.java: -------------------------------------------------------------------------------- 1 | package blog.client; 2 | 3 | import com.proto.blog.Blog; 4 | import com.proto.blog.BlogId; 5 | import com.proto.blog.BlogServiceGrpc; 6 | import io.grpc.Status; 7 | import io.grpc.stub.StreamObserver; 8 | import org.junit.jupiter.api.Test; 9 | import utils.ClientTestBase; 10 | 11 | import java.io.IOException; 12 | 13 | import static org.junit.jupiter.api.Assertions.assertNotNull; 14 | import static org.junit.jupiter.api.Assertions.assertNull; 15 | 16 | public class BlogReadTest extends ClientTestBase { 17 | 18 | private static class ReadSuccessBlogServer extends BlogServiceGrpc.BlogServiceImplBase { 19 | 20 | @Override 21 | public void readBlog(BlogId request, StreamObserver responseObserver) { 22 | responseObserver.onNext(Blog.getDefaultInstance()); 23 | responseObserver.onCompleted(); 24 | } 25 | } 26 | 27 | private static class ReadFailBlogServer extends BlogServiceGrpc.BlogServiceImplBase { 28 | @Override 29 | public void readBlog(BlogId request, StreamObserver responseObserver) { 30 | responseObserver.onError(Status.NOT_FOUND.asRuntimeException()); 31 | } 32 | } 33 | 34 | private final BlogId fakeBlogId = BlogId.newBuilder().setId("this_is_an_id").build(); 35 | 36 | @Test 37 | void readTest() throws IOException { 38 | createServerWithService(new ReadSuccessBlogServer(), BlogServiceGrpc::newBlockingStub); 39 | 40 | assertNotNull(BlogClient.readBlog(getStub(), fakeBlogId)); 41 | } 42 | 43 | @Test 44 | void deleteFailTest() throws IOException { 45 | createServerWithService(new ReadFailBlogServer(), BlogServiceGrpc::newBlockingStub); 46 | 47 | assertNull(BlogClient.readBlog(getStub(), fakeBlogId)); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/test/java/blog/client/BlogUpdateTest.java: -------------------------------------------------------------------------------- 1 | package blog.client; 2 | 3 | import com.google.protobuf.Empty; 4 | import com.proto.blog.Blog; 5 | import com.proto.blog.BlogId; 6 | import com.proto.blog.BlogServiceGrpc; 7 | import io.grpc.Status; 8 | import io.grpc.stub.StreamObserver; 9 | import org.junit.jupiter.api.Test; 10 | import utils.ClientTestBase; 11 | 12 | import java.io.IOException; 13 | 14 | import static org.junit.jupiter.api.Assertions.assertNotNull; 15 | import static org.junit.jupiter.api.Assertions.assertNull; 16 | 17 | public class BlogUpdateTest extends ClientTestBase { 18 | 19 | private static class UpdateSuccessBlogServer extends BlogServiceGrpc.BlogServiceImplBase { 20 | 21 | @Override 22 | public void updateBlog(Blog request, StreamObserver responseObserver) { 23 | responseObserver.onNext(Empty.getDefaultInstance()); 24 | responseObserver.onCompleted(); 25 | } 26 | } 27 | 28 | private static class UpdateFailBlogServer extends BlogServiceGrpc.BlogServiceImplBase { 29 | @Override 30 | public void updateBlog(Blog request, StreamObserver responseObserver) { 31 | responseObserver.onError(Status.NOT_FOUND.asRuntimeException()); 32 | } 33 | } 34 | 35 | private final BlogId blogId = BlogId.newBuilder().setId("this_is_an_id").build(); 36 | 37 | @Test 38 | void updateTest() throws IOException { 39 | createServerWithService(new UpdateSuccessBlogServer(), BlogServiceGrpc::newBlockingStub); 40 | 41 | assertNotNull(BlogClient.updateBlog(getStub(), blogId)); 42 | } 43 | 44 | @Test 45 | void updateFailTest() throws IOException { 46 | createServerWithService(new UpdateFailBlogServer(), BlogServiceGrpc::newBlockingStub); 47 | 48 | assertNull(BlogClient.updateBlog(getStub(), blogId)); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/java/blog/server/BlogCreateTest.java: -------------------------------------------------------------------------------- 1 | package blog.server; 2 | 3 | import com.mongodb.MongoException; 4 | import com.mongodb.client.MongoClient; 5 | import com.mongodb.client.MongoCollection; 6 | import com.mongodb.client.MongoDatabase; 7 | import com.mongodb.client.result.InsertOneResult; 8 | import com.proto.blog.Blog; 9 | import com.proto.blog.BlogId; 10 | import com.proto.blog.BlogServiceGrpc; 11 | import io.grpc.Status; 12 | import io.grpc.StatusRuntimeException; 13 | import org.bson.BsonObjectId; 14 | import org.bson.Document; 15 | import org.bson.types.ObjectId; 16 | import org.junit.jupiter.api.Test; 17 | import org.mockito.Mock; 18 | import org.mockito.MockitoAnnotations; 19 | import utils.ServerTestBase; 20 | 21 | import static org.junit.jupiter.api.Assertions.*; 22 | import static org.mockito.ArgumentMatchers.any; 23 | import static org.mockito.ArgumentMatchers.anyString; 24 | import static org.mockito.Mockito.when; 25 | 26 | public class BlogCreateTest extends ServerTestBase< 27 | BlogServiceGrpc.BlogServiceBlockingStub, 28 | BlogServiceGrpc.BlogServiceStub 29 | > { 30 | 31 | @Mock 32 | private MongoClient mockClient; 33 | @Mock 34 | private MongoCollection mockCollection; 35 | @Mock 36 | private MongoDatabase mockDB; 37 | 38 | final String author = "Clement"; 39 | final String title = "My Blog"; 40 | final String content = "This is a cool blog"; 41 | 42 | BlogCreateTest() { 43 | MockitoAnnotations.openMocks(this); 44 | when(mockClient.getDatabase(anyString())).thenReturn(mockDB); 45 | when(mockDB.getCollection(anyString())).thenReturn(mockCollection); 46 | addService(new BlogServiceImpl(mockClient)); 47 | setBlockingStubInstantiator(BlogServiceGrpc::newBlockingStub); 48 | } 49 | 50 | @Test 51 | void createTest() { 52 | ObjectId id = new ObjectId("579397d20c2dd41b9a8a09eb"); 53 | 54 | Document blog = new Document() 55 | .append("author", author) 56 | .append("title", title) 57 | .append("content", content); 58 | 59 | when(mockCollection.insertOne(blog)).thenReturn(InsertOneResult.acknowledged(new BsonObjectId(id))); 60 | 61 | BlogId blogId = blockingStub.createBlog( 62 | Blog.newBuilder().setAuthor(author) 63 | .setTitle(title) 64 | .setContent(content) 65 | .build() 66 | ); 67 | 68 | assertEquals(id.toString(), blogId.getId()); 69 | } 70 | 71 | @Test 72 | @SuppressWarnings("ResultOfMethodCallIgnored") 73 | void createUnacknowledgedTest() { 74 | Document blog = new Document() 75 | .append("author", author) 76 | .append("title", title) 77 | .append("content", content); 78 | 79 | when(mockCollection.insertOne(blog)).thenReturn(InsertOneResult.unacknowledged()); 80 | 81 | try { 82 | blockingStub.createBlog( 83 | Blog.newBuilder().setAuthor(author) 84 | .setTitle(title) 85 | .setContent(content) 86 | .build() 87 | ); 88 | fail("There should be an error in this case"); 89 | } catch (StatusRuntimeException e) { 90 | Status status = Status.fromThrowable(e); 91 | 92 | assertEquals(Status.Code.INTERNAL, status.getCode()); 93 | assertEquals(BlogServiceImpl.BLOG_COULDNT_BE_CREATED, status.getDescription()); 94 | } 95 | } 96 | 97 | @Test 98 | @SuppressWarnings("ResultOfMethodCallIgnored") 99 | void createNullIdTest() { 100 | Document blog = new Document() 101 | .append("author", author) 102 | .append("title", title) 103 | .append("content", content); 104 | 105 | when(mockCollection.insertOne(blog)).thenReturn(InsertOneResult.acknowledged(null)); 106 | 107 | try { 108 | blockingStub.createBlog( 109 | Blog.newBuilder().setAuthor(author) 110 | .setTitle(title) 111 | .setContent(content) 112 | .build() 113 | ); 114 | fail("There should be an error in this case"); 115 | } catch (StatusRuntimeException e) { 116 | Status status = Status.fromThrowable(e); 117 | 118 | assertEquals(Status.Code.INTERNAL, status.getCode()); 119 | assertEquals(BlogServiceImpl.BLOG_COULDNT_BE_CREATED, status.getDescription()); 120 | } 121 | } 122 | 123 | @Test 124 | @SuppressWarnings("ResultOfMethodCallIgnored") 125 | void createErrorTest() { 126 | when(mockCollection.insertOne(any(Document.class))).thenThrow(new MongoException("A message")); 127 | 128 | try { 129 | blockingStub.createBlog( 130 | Blog.newBuilder().setAuthor(author) 131 | .setTitle(title) 132 | .setContent(content) 133 | .build() 134 | ); 135 | fail("There should be an error in this case"); 136 | } catch (StatusRuntimeException e) { 137 | Status status = Status.fromThrowable(e); 138 | 139 | assertEquals(Status.Code.INTERNAL, status.getCode()); 140 | assertNotNull(status.getDescription()); 141 | assertTrue(status.getDescription().startsWith(BlogServiceImpl.BLOG_COULDNT_BE_CREATED)); 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/test/java/blog/server/BlogDeleteTest.java: -------------------------------------------------------------------------------- 1 | package blog.server; 2 | 3 | import com.mongodb.MongoException; 4 | import com.mongodb.client.MongoClient; 5 | import com.mongodb.client.MongoCollection; 6 | import com.mongodb.client.MongoDatabase; 7 | import com.mongodb.client.result.DeleteResult; 8 | import com.proto.blog.BlogId; 9 | import com.proto.blog.BlogServiceGrpc; 10 | import io.grpc.Status; 11 | import io.grpc.StatusRuntimeException; 12 | import org.bson.Document; 13 | import org.bson.conversions.Bson; 14 | import org.junit.jupiter.api.Test; 15 | import org.mockito.Mock; 16 | import org.mockito.MockitoAnnotations; 17 | import utils.ServerTestBase; 18 | 19 | import static org.junit.jupiter.api.Assertions.*; 20 | import static org.mockito.ArgumentMatchers.any; 21 | import static org.mockito.ArgumentMatchers.anyString; 22 | import static org.mockito.Mockito.when; 23 | 24 | public class BlogDeleteTest extends ServerTestBase< 25 | BlogServiceGrpc.BlogServiceBlockingStub, 26 | BlogServiceGrpc.BlogServiceStub 27 | > { 28 | @Mock 29 | private MongoClient mockClient; 30 | @Mock 31 | private MongoCollection mockCollection; 32 | @Mock 33 | private MongoDatabase mockDB; 34 | 35 | final String id = "579397d20c2dd41b9a8a09eb"; 36 | 37 | BlogDeleteTest() { 38 | MockitoAnnotations.openMocks(this); 39 | when(mockClient.getDatabase(anyString())).thenReturn(mockDB); 40 | when(mockDB.getCollection(anyString())).thenReturn(mockCollection); 41 | addService(new BlogServiceImpl(mockClient)); 42 | setBlockingStubInstantiator(BlogServiceGrpc::newBlockingStub); 43 | } 44 | 45 | @Test 46 | void deleteTest() { 47 | when(mockCollection.deleteOne(any(Bson.class))).thenReturn(DeleteResult.acknowledged(1)); 48 | 49 | assertDoesNotThrow(() -> blockingStub.deleteBlog( 50 | BlogId.newBuilder().setId(id).build() 51 | )); 52 | } 53 | 54 | @Test 55 | @SuppressWarnings("ResultOfMethodCallIgnored") 56 | void deleteInvalidIdTest() { 57 | try { 58 | blockingStub.deleteBlog(BlogId.getDefaultInstance()); 59 | fail("There should be an error in this case"); 60 | } catch (StatusRuntimeException e) { 61 | Status status = Status.fromThrowable(e); 62 | 63 | assertEquals(Status.Code.INVALID_ARGUMENT, status.getCode()); 64 | assertEquals(BlogServiceImpl.ID_CANNOT_BE_EMPTY, status.getDescription()); 65 | } 66 | } 67 | 68 | @Test 69 | @SuppressWarnings("ResultOfMethodCallIgnored") 70 | void deleteNotAcknowledgedTest() { 71 | when(mockCollection.deleteOne(any(Bson.class))).thenReturn(DeleteResult.acknowledged(0)); 72 | 73 | try { 74 | blockingStub.deleteBlog(BlogId.newBuilder().setId(id).build()); 75 | fail("There should be an error in this case"); 76 | } catch (StatusRuntimeException e) { 77 | Status status = Status.fromThrowable(e); 78 | 79 | assertEquals(Status.Code.NOT_FOUND, status.getCode()); 80 | assertNotNull(status.getDescription()); 81 | assertTrue(status.getDescription().startsWith(BlogServiceImpl.BLOG_WAS_NOT_FOUND)); 82 | } 83 | } 84 | 85 | @Test 86 | @SuppressWarnings("ResultOfMethodCallIgnored") 87 | void deleteNotFoundTest() { 88 | when(mockCollection.deleteOne(any(Bson.class))).thenReturn(DeleteResult.unacknowledged()); 89 | 90 | try { 91 | blockingStub.deleteBlog(BlogId.newBuilder().setId(id).build()); 92 | fail("There should be an error in this case"); 93 | } catch (StatusRuntimeException e) { 94 | Status status = Status.fromThrowable(e); 95 | 96 | assertEquals(Status.Code.INTERNAL, status.getCode()); 97 | assertEquals(BlogServiceImpl.BLOG_COULDNT_BE_DELETED, status.getDescription()); 98 | } 99 | } 100 | 101 | @Test 102 | @SuppressWarnings("ResultOfMethodCallIgnored") 103 | void deleteErrorTest() { 104 | when(mockCollection.deleteOne(any(Bson.class))).thenThrow(new MongoException("A message")); 105 | 106 | try { 107 | blockingStub.deleteBlog(BlogId.newBuilder().setId(id).build()); 108 | fail("There should be an error in this case"); 109 | } catch (StatusRuntimeException e) { 110 | Status status = Status.fromThrowable(e); 111 | 112 | assertEquals(Status.Code.INTERNAL, status.getCode()); 113 | assertNotNull(status.getDescription()); 114 | assertTrue(status.getDescription().startsWith(BlogServiceImpl.BLOG_COULDNT_BE_DELETED)); 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/test/java/blog/server/BlogListTest.java: -------------------------------------------------------------------------------- 1 | package blog.server; 2 | 3 | import com.mongodb.client.*; 4 | import com.proto.blog.Blog; 5 | import com.proto.blog.BlogServiceGrpc; 6 | import org.bson.Document; 7 | import org.bson.types.ObjectId; 8 | import org.junit.jupiter.api.Test; 9 | import org.mockito.Mock; 10 | import org.mockito.MockitoAnnotations; 11 | import utils.ServerTestBase; 12 | 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | import static org.junit.jupiter.api.Assertions.assertEquals; 17 | import static org.mockito.ArgumentMatchers.anyString; 18 | import static org.mockito.Mockito.when; 19 | 20 | public class BlogListTest extends ServerTestBase< 21 | BlogServiceGrpc.BlogServiceBlockingStub, 22 | BlogServiceGrpc.BlogServiceStub 23 | > { 24 | @Mock 25 | private MongoClient mockClient; 26 | @Mock 27 | private MongoCollection mockCollection; 28 | @Mock 29 | private MongoDatabase mockDB; 30 | @Mock 31 | private FindIterable iterable; 32 | @Mock 33 | private MongoCursor cursor; 34 | 35 | private final List finalResult = new ArrayList<>(); 36 | 37 | BlogListTest() { 38 | MockitoAnnotations.openMocks(this); 39 | when(mockClient.getDatabase(anyString())).thenReturn(mockDB); 40 | when(mockDB.getCollection(anyString())).thenReturn(mockCollection); 41 | addService(new BlogServiceImpl(mockClient)); 42 | setBlockingStubInstantiator(BlogServiceGrpc::newBlockingStub); 43 | } 44 | 45 | @Test 46 | void listTest() { 47 | String author = "Clement"; 48 | String title = "My Blog"; 49 | String content = "This is a cool blog"; 50 | 51 | Document blog1 = new Document("_id", new ObjectId("579397d20c2dd41b9a8a09eb")) 52 | .append("author", author) 53 | .append("title", title) 54 | .append("content", content); 55 | Document blog2 = new Document("_id", new ObjectId("579397d20c2dd41b9a8a09ec")) 56 | .append("author", author + "2") 57 | .append("title", title + "2") 58 | .append("content", content + "2"); 59 | 60 | when(mockCollection.find()).thenReturn(iterable); 61 | when(iterable.iterator()).thenReturn(cursor); 62 | when(cursor.hasNext()).thenReturn(true, true, false); 63 | when(cursor.next()).thenReturn(blog1, blog2); 64 | 65 | blockingStub.listBlogs(com.google.protobuf.Empty.getDefaultInstance()).forEachRemaining(finalResult::add); 66 | 67 | assertEquals(2, finalResult.size()); 68 | assertEquals(author, finalResult.get(0).getAuthor()); 69 | assertEquals(author + "2", finalResult.get(1).getAuthor()); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/test/java/blog/server/BlogReadTest.java: -------------------------------------------------------------------------------- 1 | package blog.server; 2 | 3 | import com.mongodb.client.*; 4 | import com.proto.blog.Blog; 5 | import com.proto.blog.BlogId; 6 | import com.proto.blog.BlogServiceGrpc; 7 | import io.grpc.Status; 8 | import io.grpc.StatusRuntimeException; 9 | import org.bson.Document; 10 | import org.bson.conversions.Bson; 11 | import org.bson.types.ObjectId; 12 | import org.junit.jupiter.api.Test; 13 | import org.mockito.Mock; 14 | import org.mockito.MockitoAnnotations; 15 | import utils.ServerTestBase; 16 | 17 | import static org.junit.jupiter.api.Assertions.*; 18 | import static org.mockito.Mockito.*; 19 | 20 | public class BlogReadTest extends ServerTestBase< 21 | BlogServiceGrpc.BlogServiceBlockingStub, 22 | BlogServiceGrpc.BlogServiceStub 23 | > { 24 | 25 | @Mock 26 | private MongoClient mockClient; 27 | @Mock 28 | private MongoCollection mockCollection; 29 | @Mock 30 | private MongoDatabase mockDB; 31 | @Mock 32 | private FindIterable iterable; 33 | 34 | BlogReadTest() { 35 | MockitoAnnotations.openMocks(this); 36 | when(mockClient.getDatabase(anyString())).thenReturn(mockDB); 37 | when(mockDB.getCollection(anyString())).thenReturn(mockCollection); 38 | addService(new BlogServiceImpl(mockClient)); 39 | setBlockingStubInstantiator(BlogServiceGrpc::newBlockingStub); 40 | } 41 | 42 | @Test 43 | void readTest() { 44 | String id = "579397d20c2dd41b9a8a09eb"; 45 | ObjectId oid = new ObjectId(id); 46 | String author = "Clement"; 47 | String title = "My Blog"; 48 | String content = "This is a cool blog"; 49 | 50 | Document blog = new Document("_id", oid) 51 | .append("author", author) 52 | .append("title", title) 53 | .append("content", content); 54 | 55 | when(mockCollection.find(any(Bson.class))).thenReturn(iterable); 56 | when(iterable.first()).thenReturn(blog); 57 | 58 | Blog b = blockingStub.readBlog(BlogId.newBuilder().setId(id).build()); 59 | 60 | assertEquals(id, b.getId()); 61 | assertEquals(author, b.getAuthor()); 62 | assertEquals(title, b.getTitle()); 63 | assertEquals(content, b.getContent()); 64 | } 65 | 66 | @Test 67 | @SuppressWarnings("ResultOfMethodCallIgnored") 68 | void readInvalidIdTest() { 69 | try { 70 | blockingStub.readBlog(BlogId.getDefaultInstance()); 71 | fail("There should be an error in this case"); 72 | } catch (StatusRuntimeException e) { 73 | Status status = Status.fromThrowable(e); 74 | 75 | assertEquals(Status.Code.INVALID_ARGUMENT, status.getCode()); 76 | assertEquals(BlogServiceImpl.ID_CANNOT_BE_EMPTY, status.getDescription()); 77 | } 78 | } 79 | 80 | @Test 81 | @SuppressWarnings("ResultOfMethodCallIgnored") 82 | void readNotFoundTest() { 83 | String id = "579397d20c2dd41b9a8a09eb"; 84 | 85 | when(mockCollection.find(any(Bson.class))).thenReturn(iterable); 86 | when(iterable.first()).thenReturn(null); 87 | 88 | try { 89 | blockingStub.readBlog(BlogId.newBuilder().setId(id).build()); 90 | fail("There should be an error in this case"); 91 | } catch (StatusRuntimeException e) { 92 | Status status = Status.fromThrowable(e); 93 | 94 | assertEquals(Status.Code.NOT_FOUND, status.getCode()); 95 | assertNotNull(status.getDescription()); 96 | assertTrue(status.getDescription().startsWith(BlogServiceImpl.BLOG_WAS_NOT_FOUND)); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/test/java/blog/server/BlogUpdateTest.java: -------------------------------------------------------------------------------- 1 | package blog.server; 2 | 3 | import com.mongodb.client.FindIterable; 4 | import com.mongodb.client.MongoClient; 5 | import com.mongodb.client.MongoCollection; 6 | import com.mongodb.client.MongoDatabase; 7 | import com.proto.blog.Blog; 8 | import com.proto.blog.BlogServiceGrpc; 9 | import io.grpc.Status; 10 | import io.grpc.StatusRuntimeException; 11 | import org.bson.Document; 12 | import org.bson.conversions.Bson; 13 | import org.bson.types.ObjectId; 14 | import org.junit.jupiter.api.Test; 15 | import org.mockito.Mock; 16 | import org.mockito.MockitoAnnotations; 17 | import utils.ServerTestBase; 18 | 19 | import static org.junit.jupiter.api.Assertions.*; 20 | import static org.mockito.Mockito.*; 21 | 22 | public class BlogUpdateTest extends ServerTestBase< 23 | BlogServiceGrpc.BlogServiceBlockingStub, 24 | BlogServiceGrpc.BlogServiceStub 25 | > { 26 | 27 | @Mock 28 | private MongoClient mockClient; 29 | @Mock 30 | private MongoCollection mockCollection; 31 | @Mock 32 | private MongoDatabase mockDB; 33 | @Mock 34 | private FindIterable iterable; 35 | 36 | BlogUpdateTest() { 37 | MockitoAnnotations.openMocks(this); 38 | when(mockClient.getDatabase(anyString())).thenReturn(mockDB); 39 | when(mockDB.getCollection(anyString())).thenReturn(mockCollection); 40 | addService(new BlogServiceImpl(mockClient)); 41 | setBlockingStubInstantiator(BlogServiceGrpc::newBlockingStub); 42 | } 43 | 44 | @Test 45 | void updateTest() { 46 | String id = "579397d20c2dd41b9a8a09eb"; 47 | ObjectId oid = new ObjectId(id); 48 | String author = "Clement"; 49 | String title = "My Blog"; 50 | String content = "This is a cool blog"; 51 | Document blog = new Document("_id", oid) 52 | .append("author", author + "_old") 53 | .append("title", title + "_old") 54 | .append("content", content + "_old"); 55 | 56 | when(mockCollection.findOneAndUpdate(any(Bson.class), any(Bson.class))).thenReturn(blog); 57 | 58 | assertDoesNotThrow(() -> blockingStub.updateBlog( 59 | Blog.newBuilder() 60 | .setId(id) 61 | .setTitle(title) 62 | .setAuthor(author) 63 | .setContent(content) 64 | .build() 65 | )); 66 | } 67 | 68 | @Test 69 | @SuppressWarnings("ResultOfMethodCallIgnored") 70 | void readInvalidIdTest() { 71 | try { 72 | blockingStub.updateBlog(Blog.newBuilder().build()); 73 | fail("There should be an error in this case"); 74 | } catch (StatusRuntimeException e) { 75 | Status status = Status.fromThrowable(e); 76 | 77 | assertEquals(Status.Code.INVALID_ARGUMENT, status.getCode()); 78 | assertEquals(BlogServiceImpl.ID_CANNOT_BE_EMPTY, status.getDescription()); 79 | } 80 | } 81 | 82 | @Test 83 | @SuppressWarnings("ResultOfMethodCallIgnored") 84 | void updateNotFoundTest() { 85 | String id = "579397d20c2dd41b9a8a09eb"; 86 | 87 | when(mockCollection.find(any(Bson.class))).thenReturn(iterable); 88 | when(iterable.first()).thenReturn(null); 89 | 90 | try { 91 | blockingStub.updateBlog(Blog.newBuilder().setId(id).build()); 92 | fail("There should be an error in this case"); 93 | } catch (StatusRuntimeException e) { 94 | Status status = Status.fromThrowable(e); 95 | 96 | assertEquals(Status.Code.NOT_FOUND, status.getCode()); 97 | assertNotNull(status.getDescription()); 98 | assertTrue(status.getDescription().startsWith(BlogServiceImpl.BLOG_WAS_NOT_FOUND)); 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/test/java/calculator/server/CalculatorAvgTest.java: -------------------------------------------------------------------------------- 1 | package calculator.server; 2 | 3 | import com.proto.calculator.AvgRequest; 4 | import com.proto.calculator.AvgResponse; 5 | import com.proto.calculator.CalculatorServiceGrpc; 6 | import io.grpc.stub.StreamObserver; 7 | import org.junit.jupiter.api.Test; 8 | import utils.ServerTestBase; 9 | 10 | import javax.annotation.Nullable; 11 | import java.util.concurrent.CountDownLatch; 12 | import java.util.concurrent.TimeUnit; 13 | import java.util.stream.IntStream; 14 | 15 | import static org.junit.jupiter.api.Assertions.*; 16 | 17 | public class CalculatorAvgTest extends ServerTestBase< 18 | CalculatorServiceGrpc.CalculatorServiceBlockingStub, 19 | CalculatorServiceGrpc.CalculatorServiceStub 20 | > { 21 | private double finalResult; 22 | 23 | @Nullable 24 | private Throwable error = null; 25 | 26 | CalculatorAvgTest() { 27 | addService(new CalculatorServiceImpl()); 28 | setAsyncStubInstantiator(CalculatorServiceGrpc::newStub); 29 | } 30 | 31 | @Test 32 | void avgTest() throws InterruptedException { 33 | CountDownLatch latch = new CountDownLatch(1); 34 | 35 | StreamObserver stream = asyncStub.avg(new StreamObserver() { 36 | @Override 37 | public void onNext(AvgResponse response) { 38 | finalResult = response.getResult(); 39 | } 40 | 41 | @Override 42 | public void onError(Throwable t) { 43 | error = t; 44 | } 45 | 46 | @Override 47 | public void onCompleted() { 48 | latch.countDown(); 49 | } 50 | }); 51 | 52 | IntStream.range(1, 11).forEach(number -> 53 | stream.onNext(AvgRequest.newBuilder().setNumber(number).build()) 54 | ); 55 | 56 | stream.onCompleted(); 57 | 58 | boolean reachedZero = latch.await(3, TimeUnit.SECONDS); 59 | 60 | assertTrue(reachedZero); 61 | assertEquals(5.5, finalResult); 62 | assertNull(error); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/test/java/calculator/server/CalculatorMaxTest.java: -------------------------------------------------------------------------------- 1 | package calculator.server; 2 | 3 | import com.proto.calculator.CalculatorServiceGrpc; 4 | import com.proto.calculator.MaxRequest; 5 | import com.proto.calculator.MaxResponse; 6 | import io.grpc.stub.StreamObserver; 7 | import org.junit.jupiter.api.Test; 8 | import utils.ServerTestBase; 9 | 10 | import javax.annotation.Nullable; 11 | import java.util.ArrayList; 12 | import java.util.Collections; 13 | import java.util.List; 14 | import java.util.concurrent.CountDownLatch; 15 | import java.util.concurrent.TimeUnit; 16 | 17 | import static org.junit.jupiter.api.Assertions.*; 18 | 19 | public class CalculatorMaxTest extends ServerTestBase< 20 | CalculatorServiceGrpc.CalculatorServiceBlockingStub, 21 | CalculatorServiceGrpc.CalculatorServiceStub 22 | > { 23 | private final List finalResult = new ArrayList<>(); 24 | 25 | @Nullable 26 | private Throwable error = null; 27 | 28 | CalculatorMaxTest() { 29 | addService(new CalculatorServiceImpl()); 30 | setAsyncStubInstantiator(CalculatorServiceGrpc::newStub); 31 | } 32 | 33 | @Test 34 | void maxTest() throws InterruptedException { 35 | List numbers = new ArrayList<>(); 36 | CountDownLatch latch = new CountDownLatch(1); 37 | 38 | Collections.addAll(numbers, 1, 5, 3, 6, 2, 20); 39 | 40 | StreamObserver stream = asyncStub.max(new StreamObserver() { 41 | @Override 42 | public void onNext(MaxResponse response) { 43 | finalResult.add(response.getMax()); 44 | } 45 | 46 | @Override 47 | public void onError(Throwable t) { 48 | error = t; 49 | } 50 | 51 | @Override 52 | public void onCompleted() { 53 | latch.countDown(); 54 | } 55 | }); 56 | 57 | for (int number : numbers) { 58 | stream.onNext(MaxRequest.newBuilder().setNumber(number).build()); 59 | } 60 | 61 | stream.onCompleted(); 62 | 63 | boolean reachedZero = latch.await(3, TimeUnit.SECONDS); 64 | List expected = new ArrayList<>(); 65 | 66 | Collections.addAll(expected, 1, 5, 6, 20); 67 | 68 | assertTrue(reachedZero); 69 | assertEquals(finalResult, expected); 70 | assertNull(error); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/test/java/calculator/server/CalculatorPrimesTest.java: -------------------------------------------------------------------------------- 1 | package calculator.server; 2 | 3 | import com.proto.calculator.CalculatorServiceGrpc; 4 | import com.proto.calculator.PrimeRequest; 5 | import org.junit.jupiter.api.Test; 6 | import utils.ServerTestBase; 7 | 8 | import java.util.ArrayList; 9 | import java.util.Collections; 10 | import java.util.List; 11 | 12 | import static org.junit.jupiter.api.Assertions.assertEquals; 13 | 14 | public class CalculatorPrimesTest extends ServerTestBase< 15 | CalculatorServiceGrpc.CalculatorServiceBlockingStub, 16 | CalculatorServiceGrpc.CalculatorServiceStub 17 | > { 18 | CalculatorPrimesTest() { 19 | addService(new CalculatorServiceImpl()); 20 | setBlockingStubInstantiator(CalculatorServiceGrpc::newBlockingStub); 21 | } 22 | 23 | private final List results = new ArrayList<>(); 24 | 25 | @Test 26 | void primesTest() { 27 | blockingStub.primes(PrimeRequest.newBuilder().setNumber(567890).build()).forEachRemaining(prime -> 28 | results.add(prime.getPrimeFactor()) 29 | ); 30 | 31 | List primes = new ArrayList<>(); 32 | Collections.addAll(primes, 2, 5, 109, 521); 33 | 34 | assertEquals(results, primes); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/calculator/server/CalculatorSqrtTest.java: -------------------------------------------------------------------------------- 1 | package calculator.server; 2 | 3 | import com.proto.calculator.CalculatorServiceGrpc; 4 | import com.proto.calculator.SqrtRequest; 5 | import com.proto.calculator.SqrtResponse; 6 | import io.grpc.Status; 7 | import io.grpc.StatusRuntimeException; 8 | import org.junit.jupiter.api.Test; 9 | import utils.ServerTestBase; 10 | 11 | import static org.junit.jupiter.api.Assertions.assertEquals; 12 | import static org.junit.jupiter.api.Assertions.fail; 13 | 14 | public class CalculatorSqrtTest extends ServerTestBase< 15 | CalculatorServiceGrpc.CalculatorServiceBlockingStub, 16 | CalculatorServiceGrpc.CalculatorServiceStub 17 | > { 18 | 19 | CalculatorSqrtTest() { 20 | addService(new CalculatorServiceImpl()); 21 | setBlockingStubInstantiator(CalculatorServiceGrpc::newBlockingStub); 22 | } 23 | 24 | @Test 25 | void sqrtTest() { 26 | SqrtResponse response = blockingStub.sqrt(SqrtRequest.newBuilder().setNumber(25).build()); 27 | 28 | assertEquals(5, response.getResult()); 29 | } 30 | 31 | @Test 32 | @SuppressWarnings("ResultOfMethodCallIgnored") 33 | void sqrtErrorTest() { 34 | try { 35 | blockingStub.sqrt(SqrtRequest.newBuilder().setNumber(-1).build()); 36 | fail("There should be an error in this case"); 37 | } catch (StatusRuntimeException e) { 38 | Status status = Status.fromThrowable(e); 39 | 40 | assertEquals(Status.Code.INVALID_ARGUMENT, status.getCode()); 41 | assertEquals("The number being sent cannot be negative\nNumber: -1", status.getDescription()); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/test/java/calculator/server/CalculatorSumTest.java: -------------------------------------------------------------------------------- 1 | package calculator.server; 2 | 3 | import com.proto.calculator.CalculatorServiceGrpc; 4 | import com.proto.calculator.SumRequest; 5 | import com.proto.calculator.SumResponse; 6 | import org.junit.jupiter.api.Test; 7 | import utils.ServerTestBase; 8 | 9 | import static org.junit.jupiter.api.Assertions.assertEquals; 10 | 11 | public class CalculatorSumTest extends ServerTestBase< 12 | CalculatorServiceGrpc.CalculatorServiceBlockingStub, 13 | CalculatorServiceGrpc.CalculatorServiceStub 14 | > { 15 | 16 | CalculatorSumTest() { 17 | addService(new CalculatorServiceImpl()); 18 | setBlockingStubInstantiator(CalculatorServiceGrpc::newBlockingStub); 19 | } 20 | 21 | @Test 22 | void sumTest() { 23 | SumResponse response = blockingStub.sum(SumRequest.newBuilder().setFirstNumber(1).setSecondNumber(1).build()); 24 | 25 | assertEquals(2, response.getResult()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/test/java/greeting/server/GreetingEveryoneServerTest.java: -------------------------------------------------------------------------------- 1 | package greeting.server; 2 | 3 | import com.proto.greeting.GreetingRequest; 4 | import com.proto.greeting.GreetingResponse; 5 | import com.proto.greeting.GreetingServiceGrpc; 6 | import io.grpc.stub.StreamObserver; 7 | import org.junit.jupiter.api.Test; 8 | import utils.ServerTestBase; 9 | 10 | import javax.annotation.Nullable; 11 | import java.util.ArrayList; 12 | import java.util.Collections; 13 | import java.util.List; 14 | import java.util.concurrent.CountDownLatch; 15 | import java.util.concurrent.TimeUnit; 16 | import java.util.stream.Collectors; 17 | 18 | import static org.junit.jupiter.api.Assertions.*; 19 | 20 | public class GreetingEveryoneServerTest extends ServerTestBase { 21 | 22 | private final List finalResult = new ArrayList<>(); 23 | 24 | @Nullable 25 | private Throwable error = null; 26 | 27 | GreetingEveryoneServerTest() { 28 | addService(new GreetingServiceImpl(new SleeperImpl())); 29 | setAsyncStubInstantiator(GreetingServiceGrpc::newStub); 30 | } 31 | 32 | @Test 33 | void greetEveryoneImplReplyMessage() throws InterruptedException { 34 | List names = new ArrayList<>(); 35 | CountDownLatch latch = new CountDownLatch(1); 36 | 37 | Collections.addAll(names, "Clement", "Marie", "Test"); 38 | 39 | StreamObserver stream = asyncStub.greetEveryone(new StreamObserver() { 40 | @Override 41 | public void onNext(GreetingResponse response) { 42 | finalResult.add(response.getResult()); 43 | } 44 | 45 | @Override 46 | public void onError(Throwable t) { 47 | error = t; 48 | } 49 | 50 | @Override 51 | public void onCompleted() { 52 | latch.countDown(); 53 | } 54 | }); 55 | 56 | for (String name: names) { 57 | stream.onNext(GreetingRequest.newBuilder().setFirstName(name).build()); 58 | } 59 | 60 | stream.onCompleted(); 61 | 62 | boolean reachedZero = latch.await(3, TimeUnit.SECONDS); 63 | 64 | assertTrue(reachedZero); 65 | assertEquals(finalResult, names.stream().map(name -> "Hello " + name).collect(Collectors.toList())); 66 | assertNull(error); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/test/java/greeting/server/GreetingManyTimesServerTest.java: -------------------------------------------------------------------------------- 1 | package greeting.server; 2 | 3 | import com.proto.greeting.GreetingRequest; 4 | import com.proto.greeting.GreetingServiceGrpc; 5 | import org.junit.jupiter.api.Test; 6 | import utils.ServerTestBase; 7 | 8 | import java.util.concurrent.atomic.AtomicInteger; 9 | 10 | import static org.junit.jupiter.api.Assertions.assertEquals; 11 | 12 | public class GreetingManyTimesServerTest extends ServerTestBase { 13 | 14 | GreetingManyTimesServerTest() { 15 | addService(new GreetingServiceImpl(new SleeperImpl())); 16 | setBlockingStubInstantiator(GreetingServiceGrpc::newBlockingStub); 17 | } 18 | 19 | @Test 20 | void greetManyTimesImplReplyMessage() { 21 | AtomicInteger count = new AtomicInteger(); 22 | 23 | blockingStub.greetManyTimes(GreetingRequest.newBuilder().setFirstName("Clement").build()).forEachRemaining(response -> { 24 | assertEquals("Hello Clement", response.getResult()); 25 | count.incrementAndGet(); 26 | }); 27 | 28 | assertEquals(count.get(), 10); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/test/java/greeting/server/GreetingServerTest.java: -------------------------------------------------------------------------------- 1 | package greeting.server; 2 | 3 | import com.proto.greeting.GreetingRequest; 4 | import com.proto.greeting.GreetingResponse; 5 | import com.proto.greeting.GreetingServiceGrpc; 6 | import org.junit.jupiter.api.Test; 7 | import utils.ServerTestBase; 8 | 9 | import static org.junit.jupiter.api.Assertions.assertEquals; 10 | 11 | public class GreetingServerTest extends ServerTestBase { 12 | 13 | GreetingServerTest() { 14 | addService(new GreetingServiceImpl(new SleeperImpl())); 15 | setBlockingStubInstantiator(GreetingServiceGrpc::newBlockingStub); 16 | } 17 | 18 | @Test 19 | void greetImplReplyMessage() { 20 | GreetingResponse response = blockingStub.greet(GreetingRequest.newBuilder().setFirstName("Clement").build()); 21 | 22 | assertEquals("Hello Clement", response.getResult()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/test/java/greeting/server/GreetingWithDeadlineTest.java: -------------------------------------------------------------------------------- 1 | package greeting.server; 2 | 3 | import com.proto.greeting.GreetingRequest; 4 | import com.proto.greeting.GreetingResponse; 5 | import com.proto.greeting.GreetingServiceGrpc; 6 | import io.grpc.Deadline; 7 | import io.grpc.Status; 8 | import io.grpc.StatusRuntimeException; 9 | import org.junit.jupiter.api.Test; 10 | import utils.ServerTestBase; 11 | 12 | import java.util.concurrent.TimeUnit; 13 | 14 | import static org.junit.jupiter.api.Assertions.assertEquals; 15 | import static org.junit.jupiter.api.Assertions.fail; 16 | 17 | public class GreetingWithDeadlineTest extends ServerTestBase< 18 | GreetingServiceGrpc.GreetingServiceBlockingStub, 19 | GreetingServiceGrpc.GreetingServiceStub 20 | > { 21 | GreetingWithDeadlineTest() { 22 | addService(new GreetingServiceImpl(new SleeperImpl())); 23 | setBlockingStubInstantiator(GreetingServiceGrpc::newBlockingStub); 24 | } 25 | 26 | @Test 27 | void greetTest() { 28 | GreetingResponse response = blockingStub.withDeadline(Deadline.after(3, TimeUnit.SECONDS)) 29 | .greetWithDeadline(GreetingRequest.newBuilder().setFirstName("Clement").build()); 30 | 31 | assertEquals("Hello Clement", response.getResult()); 32 | } 33 | 34 | @Test 35 | @SuppressWarnings("ResultOfMethodCallIgnored") 36 | void greetDeadlineExceededTest() { 37 | try { 38 | blockingStub.withDeadline(Deadline.after(100, TimeUnit.MILLISECONDS)) 39 | .greetWithDeadline(GreetingRequest.newBuilder().setFirstName("Clement").build()); 40 | fail("There should be an error in this case"); 41 | } catch (StatusRuntimeException e) { 42 | Status status = Status.fromThrowable(e); 43 | 44 | assertEquals(Status.Code.DEADLINE_EXCEEDED, status.getCode()); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/test/java/greeting/server/GreetingWithDeadlineThrowsTest.java: -------------------------------------------------------------------------------- 1 | package greeting.server; 2 | 3 | import com.proto.greeting.GreetingRequest; 4 | import com.proto.greeting.GreetingServiceGrpc; 5 | import io.grpc.*; 6 | import io.grpc.inprocess.InProcessChannelBuilder; 7 | import io.grpc.inprocess.InProcessServerBuilder; 8 | import org.junit.After; 9 | import org.junit.jupiter.api.Test; 10 | import utils.Sleeper; 11 | 12 | import java.io.IOException; 13 | import java.util.concurrent.TimeUnit; 14 | 15 | import static org.junit.jupiter.api.Assertions.fail; 16 | 17 | public class GreetingWithDeadlineThrowsTest { 18 | 19 | private static class ThrowingSleeper implements Sleeper { 20 | @Override 21 | public void sleep(long millis) throws InterruptedException { 22 | throw new InterruptedException(); 23 | } 24 | } 25 | 26 | private Server server; 27 | private ManagedChannel channel; 28 | 29 | @After 30 | public void after() throws InterruptedException { 31 | if (!server.isShutdown()) 32 | server.shutdownNow().awaitTermination(5, TimeUnit.SECONDS); 33 | 34 | if (!channel.isShutdown()) 35 | channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS); 36 | } 37 | 38 | @Test 39 | @SuppressWarnings("ResultOfMethodCallIgnored") 40 | void greetThrows() throws IOException { 41 | String serverName = InProcessServerBuilder.generateName(); 42 | server = InProcessServerBuilder.forName(serverName) 43 | .directExecutor() 44 | .addService(new GreetingServiceImpl(new ThrowingSleeper())) 45 | .build() 46 | .start(); 47 | channel = InProcessChannelBuilder.forName(serverName) 48 | .directExecutor() 49 | .build(); 50 | GreetingServiceGrpc.GreetingServiceBlockingStub stub = GreetingServiceGrpc.newBlockingStub(channel); 51 | 52 | try { 53 | stub.withDeadline(Deadline.after(100, TimeUnit.MILLISECONDS)) 54 | .greetWithDeadline(GreetingRequest.newBuilder().setFirstName("Clement").build()); 55 | fail("There should be an error in this case"); 56 | } catch (RuntimeException e) { 57 | // success 58 | channel.shutdownNow(); 59 | server.shutdownNow(); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/test/java/greeting/server/InterceptorTest.java: -------------------------------------------------------------------------------- 1 | package greeting.server; 2 | 3 | import com.proto.greeting.GreetingRequest; 4 | import com.proto.greeting.GreetingResponse; 5 | import com.proto.greeting.GreetingServiceGrpc; 6 | import greeting.client.AddHeaderInterceptor; 7 | import io.grpc.*; 8 | import io.grpc.inprocess.InProcessChannelBuilder; 9 | import io.grpc.inprocess.InProcessServerBuilder; 10 | import org.junit.jupiter.api.Test; 11 | 12 | import java.io.IOException; 13 | import java.util.concurrent.TimeUnit; 14 | 15 | import static org.junit.jupiter.api.Assertions.assertEquals; 16 | import static org.junit.jupiter.api.Assertions.fail; 17 | 18 | public class InterceptorTest { 19 | 20 | @Test 21 | void interceptPassingTest() throws IOException, InterruptedException { 22 | String serverName = "test"; 23 | InProcessServerBuilder builder = InProcessServerBuilder.forName(serverName) 24 | .addService(new GreetingServiceImpl(new SleeperImpl())) 25 | .intercept(new HeaderCheckInterceptor()); 26 | 27 | Server server = builder.build(); 28 | server.start(); 29 | 30 | ManagedChannel channel = InProcessChannelBuilder.forName(serverName) 31 | .directExecutor() 32 | .usePlaintext() 33 | .intercept(new AddHeaderInterceptor()) 34 | .build(); 35 | 36 | GreetingServiceGrpc.GreetingServiceBlockingStub blockingStub = GreetingServiceGrpc.newBlockingStub(channel); 37 | 38 | GreetingResponse response = blockingStub.greet(GreetingRequest.newBuilder().setFirstName("Clement").build()); 39 | 40 | assertEquals("Hello Clement", response.getResult()); 41 | if (!server.isShutdown()) 42 | server.shutdownNow().awaitTermination(5, TimeUnit.SECONDS); 43 | 44 | if (!channel.isShutdown()) 45 | channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS); 46 | } 47 | 48 | @Test 49 | @SuppressWarnings("ResultOfMethodCallIgnored") 50 | void interceptErrorTest() throws IOException, InterruptedException { 51 | String serverName = "test"; 52 | InProcessServerBuilder builder = InProcessServerBuilder.forName(serverName) 53 | .addService(new GreetingServiceImpl(new SleeperImpl())) 54 | .intercept(new HeaderCheckInterceptor()); 55 | 56 | Server server = builder.build(); 57 | server.start(); 58 | 59 | ManagedChannel channel = InProcessChannelBuilder.forName(serverName) 60 | .directExecutor() 61 | .usePlaintext() 62 | .build(); 63 | 64 | GreetingServiceGrpc.GreetingServiceBlockingStub blockingStub = GreetingServiceGrpc.newBlockingStub(channel); 65 | 66 | try { 67 | blockingStub.greet(GreetingRequest.newBuilder().setFirstName("Clement").build()); 68 | fail("There should be an error in this case"); 69 | } catch (StatusRuntimeException e) { 70 | Status status = Status.fromThrowable(e); 71 | 72 | assertEquals(Status.Code.CANCELLED, status.getCode()); 73 | } 74 | 75 | if (!server.isShutdown()) 76 | server.shutdownNow().awaitTermination(5, TimeUnit.SECONDS); 77 | 78 | if (!channel.isShutdown()) 79 | channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/test/java/greeting/server/LongGreetingServerTest.java: -------------------------------------------------------------------------------- 1 | package greeting.server; 2 | 3 | import com.proto.greeting.GreetingRequest; 4 | import com.proto.greeting.GreetingResponse; 5 | import com.proto.greeting.GreetingServiceGrpc; 6 | import io.grpc.stub.StreamObserver; 7 | import org.junit.jupiter.api.Test; 8 | import utils.ServerTestBase; 9 | 10 | import javax.annotation.Nullable; 11 | import java.util.ArrayList; 12 | import java.util.Collections; 13 | import java.util.List; 14 | import java.util.concurrent.CountDownLatch; 15 | import java.util.concurrent.TimeUnit; 16 | 17 | import static org.junit.jupiter.api.Assertions.*; 18 | 19 | public class LongGreetingServerTest extends ServerTestBase { 20 | 21 | private String finalResult = ""; 22 | 23 | @Nullable 24 | private Throwable error = null; 25 | 26 | LongGreetingServerTest() { 27 | addService(new GreetingServiceImpl(new SleeperImpl())); 28 | setAsyncStubInstantiator(GreetingServiceGrpc::newStub); 29 | } 30 | 31 | @Test 32 | void longGreetImplReplyMessage() throws InterruptedException { 33 | List names = new ArrayList<>(); 34 | CountDownLatch latch = new CountDownLatch(1); 35 | 36 | Collections.addAll(names, "Clement", "Marie", "Test"); 37 | 38 | StreamObserver stream = asyncStub.longGreet(new StreamObserver() { 39 | @Override 40 | public void onNext(GreetingResponse response) { 41 | finalResult = response.getResult(); 42 | } 43 | 44 | @Override 45 | public void onError(Throwable t) { 46 | error = t; 47 | } 48 | 49 | @Override 50 | public void onCompleted() { 51 | latch.countDown(); 52 | } 53 | }); 54 | 55 | for (String name: names) { 56 | stream.onNext(GreetingRequest.newBuilder().setFirstName(name).build()); 57 | } 58 | 59 | stream.onCompleted(); 60 | 61 | boolean reachedZero = latch.await(3, TimeUnit.SECONDS); 62 | 63 | assertTrue(reachedZero); 64 | assertEquals("Hello Clement!\nHello Marie!\nHello Test!\n", finalResult); 65 | assertNull(error); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/test/java/utils/ClientTestBase.java: -------------------------------------------------------------------------------- 1 | package utils; 2 | 3 | import io.grpc.BindableService; 4 | import io.grpc.ManagedChannel; 5 | import io.grpc.Server; 6 | import io.grpc.inprocess.InProcessChannelBuilder; 7 | import io.grpc.inprocess.InProcessServerBuilder; 8 | import io.grpc.stub.AbstractStub; 9 | import org.junit.jupiter.api.AfterEach; 10 | 11 | import java.io.IOException; 12 | import java.util.concurrent.TimeUnit; 13 | 14 | @SuppressWarnings("rawtypes") 15 | public class ClientTestBase { 16 | 17 | private Server server; 18 | private ManagedChannel channel; 19 | private STUB stub; 20 | 21 | @AfterEach 22 | void after() throws InterruptedException { 23 | if (!server.isShutdown()) 24 | server.shutdownNow().awaitTermination(5, TimeUnit.SECONDS); 25 | 26 | if (!channel.isShutdown()) 27 | channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS); 28 | } 29 | 30 | protected STUB getStub() { return stub; } 31 | 32 | protected void createServerWithService( 33 | BindableService service, 34 | StubInstantiator instantiator 35 | ) throws IOException { 36 | String serverName = InProcessServerBuilder.generateName(); 37 | server = InProcessServerBuilder.forName(serverName) 38 | .directExecutor() 39 | .addService(service) 40 | .build() 41 | .start(); 42 | channel = InProcessChannelBuilder.forName(serverName) 43 | .directExecutor() 44 | .build(); 45 | stub = instantiator.instantiate(channel); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/test/java/utils/ServerTestBase.java: -------------------------------------------------------------------------------- 1 | package utils; 2 | 3 | import io.grpc.BindableService; 4 | import io.grpc.ManagedChannel; 5 | import io.grpc.Server; 6 | import io.grpc.inprocess.InProcessChannelBuilder; 7 | import io.grpc.inprocess.InProcessServerBuilder; 8 | import io.grpc.stub.AbstractAsyncStub; 9 | import io.grpc.stub.AbstractBlockingStub; 10 | import org.junit.jupiter.api.AfterEach; 11 | import org.junit.jupiter.api.BeforeEach; 12 | 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | import java.util.concurrent.TimeUnit; 16 | 17 | @SuppressWarnings("rawtypes") 18 | public class ServerTestBase { 19 | private static final String serverName = "test"; 20 | 21 | private Server server; 22 | private ManagedChannel channel; 23 | private StubInstantiator blockingStubInstantiator = null; 24 | private StubInstantiator asyncStubInstantiator = null; 25 | protected BLOCKING_STUB blockingStub = null; 26 | protected ASYNC_STUB asyncStub = null; 27 | 28 | private final List services = new ArrayList<>(); 29 | 30 | protected void addService(BindableService service) { 31 | this.services.add(service); 32 | } 33 | 34 | protected void setBlockingStubInstantiator(StubInstantiator stub) { 35 | this.blockingStubInstantiator = stub; 36 | } 37 | 38 | protected void setAsyncStubInstantiator(StubInstantiator stub) { 39 | this.asyncStubInstantiator = stub; 40 | } 41 | 42 | @BeforeEach 43 | void before() throws Throwable { 44 | InProcessServerBuilder builder = InProcessServerBuilder.forName(serverName); 45 | 46 | for (BindableService service : services) 47 | builder.addService(service); 48 | 49 | server = builder.build(); 50 | server.start(); 51 | 52 | channel = InProcessChannelBuilder.forName(serverName) 53 | .directExecutor() 54 | .usePlaintext() 55 | .build(); 56 | 57 | if (blockingStubInstantiator != null) 58 | blockingStub = blockingStubInstantiator.instantiate(channel); 59 | if (asyncStubInstantiator != null) 60 | asyncStub = asyncStubInstantiator.instantiate(channel); 61 | 62 | if (asyncStub == null && blockingStub == null) 63 | throw new Throwable("Either blockingStub or asyncStub need to be initialized."); 64 | } 65 | 66 | @AfterEach 67 | void after() throws InterruptedException { 68 | if (!server.isShutdown()) 69 | server.shutdownNow().awaitTermination(5, TimeUnit.SECONDS); 70 | 71 | if (!channel.isShutdown()) 72 | channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/test/java/utils/StubInstantiator.java: -------------------------------------------------------------------------------- 1 | package utils; 2 | 3 | import io.grpc.ManagedChannel; 4 | import io.grpc.stub.AbstractStub; 5 | 6 | @SuppressWarnings("rawtypes") 7 | public interface StubInstantiator { 8 | STUB instantiate(ManagedChannel channel); 9 | } -------------------------------------------------------------------------------- /ssl/README.md: -------------------------------------------------------------------------------- 1 | # SSL 2 | 3 | ## OpenSSL 4 | 5 | Check if you already have OpenSSL installed: 6 | 7 | ```shell 8 | openssl version 9 | ``` 10 | 11 | If this gives you an error, please follow instructions [here](https://github.com/openssl/openssl) to install it. 12 | 13 | ## Run 14 | 15 | ```shell 16 | chmod +x ssl.sh 17 | ./ssl.sh 18 | ``` -------------------------------------------------------------------------------- /ssl/ssl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Output files 3 | # ca.key: Certificate Authority private key file (this shouldn't be shared in real-life) 4 | # ca.crt: Certificate Authority trust certificate (this should be shared with users in real-life) 5 | # server.key: Server private key, password protected (this shouldn't be shared) 6 | # server.csr: Server certificate signing request (this should be shared with the CA owner) 7 | # server.crt: Server certificate signed by the CA (this would be sent back by the CA owner) - keep on server 8 | # server.pem: Conversion of server.key into a format gRPC likes (this shouldn't be shared) 9 | 10 | # Summary 11 | # Private files: ca.key, server.key, server.pem, server.crt 12 | # "Share" files: ca.crt (needed by the client), server.csr (needed by the CA) 13 | 14 | # Changes these CN's to match your hosts in your environment if needed. 15 | SERVER_CN=localhost 16 | 17 | # Step 1: Generate Certificate Authority + Trust Certificate (ca.crt) 18 | openssl genrsa -passout pass:1111 -des3 -out ca.key 4096 19 | openssl req -passin pass:1111 -new -x509 -days 365 -key ca.key -out ca.crt -subj "/CN=ca" 20 | 21 | # Step 2: Generate the Server Private Key (server.key) 22 | openssl genrsa -passout pass:1111 -des3 -out server.key 4096 23 | 24 | # Step 3: Get a certificate signing request from the CA (server.csr) 25 | openssl req -passin pass:1111 -new -key server.key -out server.csr -subj "/CN=${SERVER_CN}" 26 | 27 | # Step 4: Sign the certificate with the CA we created (it's called self signing) - server.crt 28 | openssl x509 -req -extfile <(printf "subjectAltName=DNS:%s" ${SERVER_CN}) -passin pass:1111 -days 365 -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt 29 | 30 | # Step 5: Convert the server certificate to .pem format (server.pem) - usable by gRPC 31 | openssl pkcs8 -topk8 -nocrypt -passin pass:1111 -in server.key -out server.pem --------------------------------------------------------------------------------