├── NOTICE ├── project ├── build.properties └── plugins.sbt ├── .travis.yml ├── .gitignore ├── .scalafmt.conf ├── protocol └── src │ └── main │ ├── protobuf │ ├── clock.proto │ ├── sum.proto │ ├── helloworld.proto │ └── customizations.proto │ └── scala │ └── io │ └── ontherocks │ └── hellogrpc │ ├── RockingMessage.scala │ ├── package.scala │ └── Birthday.scala ├── demo └── src │ └── main │ └── scala │ └── io │ └── ontherocks │ └── hellogrpc │ ├── package.scala │ ├── GrpcServer.scala │ ├── HelloWorldServer.scala │ ├── ClockServer.scala │ ├── SumServer.scala │ ├── SumClient.scala │ ├── HelloWorldClient.scala │ └── ClockClient.scala └── README.md /NOTICE: -------------------------------------------------------------------------------- 1 | Copyright 2017 Petra Bierleutgeb 2 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version = 1.1.4 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: scala 2 | 3 | scala: 4 | - 2.12.4 5 | 6 | jdk: 7 | - oraclejdk8 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Project-specific .gitignore 2 | # 3 | # Everything specific to your OS, IDE, etc.. should go into your global .gitignore 4 | # (usually found at $HOME/.gitignore) 5 | -------------------------------------------------------------------------------- /.scalafmt.conf: -------------------------------------------------------------------------------- 1 | style = defaultWithAlign 2 | 3 | danglingParentheses = true 4 | indentOperator = spray 5 | maxColumn = 100 6 | project.excludeFilters = [".*\\.sbt"] 7 | rewrite.rules = [RedundantBraces, RedundantParens, SortImports] 8 | spaces.inImportCurlyBraces = true 9 | unindentTopLevelOperators = true 10 | -------------------------------------------------------------------------------- /protocol/src/main/protobuf/clock.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package io.ontherocks.hellogrpc; 4 | 5 | /* 6 | * Continuously returns the current time in milliseconds. 7 | */ 8 | service Clock { 9 | rpc StreamTime(TimeRequest) returns (stream TimeResponse) {} 10 | } 11 | 12 | message TimeRequest {} 13 | 14 | message TimeResponse { 15 | int64 currentTime = 1; 16 | } -------------------------------------------------------------------------------- /protocol/src/main/protobuf/sum.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package io.ontherocks.hellogrpc; 4 | 5 | /* 6 | * Sums up numbers received from the client and returns the current result after each received request. 7 | */ 8 | service Sum { 9 | rpc Add(stream SumRequest) returns (stream SumResponse) {} 10 | } 11 | 12 | message SumRequest { 13 | int32 toAdd = 1; 14 | } 15 | 16 | message SumResponse { 17 | int32 currentResult = 1; 18 | } -------------------------------------------------------------------------------- /protocol/src/main/protobuf/helloworld.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | import "google/protobuf/wrappers.proto"; 4 | import "scalapb/scalapb.proto"; 5 | 6 | package io.ontherocks.hellogrpc; 7 | 8 | /* 9 | * Returns a greeting for the given person optionally including a custom message. 10 | */ 11 | service HelloWorld { 12 | rpc SayHello(ToBeGreeted) returns (Greeting) {} 13 | } 14 | 15 | message ToBeGreeted { 16 | message Person { 17 | string name = 1; 18 | } 19 | 20 | Person person = 1; 21 | google.protobuf.StringValue msg = 2; 22 | } 23 | 24 | message Greeting { 25 | string message = 1; 26 | } -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("org.foundweekends" % "sbt-bintray" % "0.5.1") 2 | addSbtPlugin("io.get-coursier" % "sbt-coursier" % "1.0.0-RC13") 3 | addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "0.9.3") 4 | addSbtPlugin("de.heikoseeberger" % "sbt-header" % "3.0.2") 5 | addSbtPlugin("com.thesamet" % "sbt-protoc" % "0.99.12") 6 | addSbtPlugin("com.lucidchart" % "sbt-scalafmt-coursier" % "1.14") 7 | 8 | libraryDependencies += "com.trueaccord.scalapb" %% "compilerplugin" % "0.6.6" 9 | libraryDependencies += "org.slf4j" % "slf4j-nop" % "1.7.25" // Needed by sbt-git 10 | -------------------------------------------------------------------------------- /protocol/src/main/scala/io/ontherocks/hellogrpc/RockingMessage.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Petra Bierleutgeb 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.ontherocks.hellogrpc 18 | 19 | trait RockingMessage { 20 | 21 | val rocking = "I rock!" 22 | 23 | } 24 | -------------------------------------------------------------------------------- /demo/src/main/scala/io/ontherocks/hellogrpc/package.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Petra Bierleutgeb 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.ontherocks 18 | 19 | package object hellogrpc { 20 | 21 | type Traversable[+A] = scala.collection.immutable.Traversable[A] 22 | type Iterable[+A] = scala.collection.immutable.Iterable[A] 23 | type Seq[+A] = scala.collection.immutable.Seq[A] 24 | type IndexedSeq[+A] = scala.collection.immutable.IndexedSeq[A] 25 | } 26 | -------------------------------------------------------------------------------- /protocol/src/main/scala/io/ontherocks/hellogrpc/package.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Petra Bierleutgeb 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.ontherocks 18 | 19 | package object hellogrpc { 20 | 21 | type Traversable[+A] = scala.collection.immutable.Traversable[A] 22 | type Iterable[+A] = scala.collection.immutable.Iterable[A] 23 | type Seq[+A] = scala.collection.immutable.Seq[A] 24 | type IndexedSeq[+A] = scala.collection.immutable.IndexedSeq[A] 25 | } 26 | -------------------------------------------------------------------------------- /protocol/src/main/protobuf/customizations.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | import "google/protobuf/wrappers.proto"; 4 | import "scalapb/scalapb.proto"; 5 | 6 | package io.ontherocks.hellogrpc.customizations; 7 | 8 | option (scalapb.options) = { 9 | // use a custom Scala package name 10 | // package_name: "io.ontherocks.introgrpc.demo" 11 | 12 | // don't append file name to package 13 | flat_package: true 14 | 15 | // generate one Scala file for all messages (services still get their own file) 16 | single_file: true 17 | 18 | // add imports to generated file 19 | // useful when extending traits or using custom types 20 | // import: "io.ontherocks.hellogrpc.RockingMessage" 21 | 22 | // code to put at the top of generated file 23 | // works only with `single_file: true` 24 | preamble: "sealed trait SomeSealedTrait" 25 | }; 26 | 27 | /* 28 | * Demoes various customization options provided by ScalaPBs. 29 | */ 30 | message ToBeGreeted { 31 | option (scalapb.message).extends = "io.ontherocks.hellogrpc.RockingMessage"; 32 | 33 | message Person { 34 | string name = 1; 35 | } 36 | 37 | Person person = 1; 38 | google.protobuf.StringValue msg = 2; 39 | } 40 | 41 | message Greeting { 42 | string message = 1; 43 | } -------------------------------------------------------------------------------- /demo/src/main/scala/io/ontherocks/hellogrpc/GrpcServer.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Petra Bierleutgeb 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.ontherocks.hellogrpc 18 | 19 | import io.grpc.{ ServerBuilder, ServerServiceDefinition } 20 | 21 | trait GrpcServer { 22 | 23 | /** 24 | * Just for demo purposes 25 | */ 26 | def runServer(ssd: ServerServiceDefinition): Unit = { 27 | val server = ServerBuilder 28 | .forPort(50051) 29 | .addService(ssd) 30 | .build 31 | .start 32 | 33 | // make sure our server is stopped when jvm is shut down 34 | Runtime.getRuntime.addShutdownHook(new Thread() { 35 | override def run(): Unit = server.shutdown() 36 | }) 37 | 38 | server.awaitTermination() 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hello-grpc-scala # 2 | 3 | [![Build Status](https://travis-ci.org/pbvie/hello-grpc-scala.svg?branch=master)](https://travis-ci.org/pbvie/hello-grpc-scala) 4 | 5 | Welcome to hello-grpc-scala! 6 | 7 | ## Talks and slides 8 | 9 | The demo code in this repo has been used in talks given at ScalaDays and ScalaSwarm (both 2017). A final, updated and corrected version of the slides can be found at [slides.com/petrabierleutgeb/hello-grpc-final](https://slides.com/petrabierleutgeb/hello-grpc-final). 10 | 11 | ## Blog 12 | 13 | The corresponding blog post can be found here: [Hello gRPC! (with ScalaPB)](https://blog.codecentric.de/en/2017/01/hello-grpc-scalapb/) 14 | 15 | ## Contribution policy ## 16 | 17 | Contributions via GitHub pull requests are gladly accepted from their original 18 | author. Along with any pull requests, please state that the contribution is your 19 | original work and that you license the work to the project under the project's 20 | open source license. Whether or not you state this explicitly, by submitting any 21 | copyrighted material via pull request, email, or other means you agree to 22 | license the material under the project's open source license and warrant that 23 | you have the legal authority to do so. 24 | 25 | ## License ## 26 | 27 | This code is open source software licensed under the 28 | [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0) license. 29 | -------------------------------------------------------------------------------- /protocol/src/main/scala/io/ontherocks/hellogrpc/Birthday.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Petra Bierleutgeb 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.ontherocks.hellogrpc 18 | 19 | import com.trueaccord.scalapb.TypeMapper 20 | 21 | import scala.util.Try 22 | 23 | final case class Birthday(month: Int, day: Int) 24 | 25 | /** 26 | * Demoes how to create a type mapper. 27 | */ 28 | object Birthday { 29 | 30 | def base2custom(birthdayStr: String): Birthday = 31 | Try { 32 | val b = birthdayStr.split("-") 33 | Birthday(Integer.parseInt(b(0)), Integer.parseInt(b(1))) 34 | }.getOrElse(Birthday(1, 1)) 35 | 36 | def custom2base(birthday: Birthday): String = s"${birthday.month}-${birthday.day}" 37 | 38 | implicit val typeMapper: TypeMapper[String, Birthday] = TypeMapper(base2custom)(custom2base) 39 | 40 | } 41 | -------------------------------------------------------------------------------- /demo/src/main/scala/io/ontherocks/hellogrpc/HelloWorldServer.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Petra Bierleutgeb 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.ontherocks.hellogrpc 18 | 19 | import io.ontherocks.hellogrpc.helloworld.{ Greeting, HelloWorldGrpc, ToBeGreeted } 20 | 21 | import scala.concurrent.{ ExecutionContext, Future } 22 | 23 | object HelloWorldServer extends GrpcServer { 24 | 25 | class HelloWorldService extends HelloWorldGrpc.HelloWorld { 26 | def sayHello(request: ToBeGreeted): Future[Greeting] = { 27 | val greetedPerson = request.person match { 28 | case Some(person) => person.name 29 | case None => "anonymous" 30 | } 31 | Future.successful(Greeting(message = s"Hello ${greetedPerson}!")) 32 | } 33 | } 34 | 35 | def main(args: Array[String]): Unit = { 36 | val ssd = HelloWorldGrpc.bindService(new HelloWorldService(), ExecutionContext.global) 37 | runServer(ssd) 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /demo/src/main/scala/io/ontherocks/hellogrpc/ClockServer.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Petra Bierleutgeb 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.ontherocks.hellogrpc 18 | 19 | import io.grpc.stub.StreamObserver 20 | import io.ontherocks.hellogrpc.clock.ClockGrpc.Clock 21 | import io.ontherocks.hellogrpc.clock.{ ClockGrpc, TimeRequest, TimeResponse } 22 | import monix.execution.Scheduler 23 | import monix.execution.Scheduler.{ global => scheduler } 24 | 25 | import scala.concurrent.duration._ 26 | 27 | object ClockServer extends GrpcServer { 28 | 29 | class ClockService extends Clock { 30 | def streamTime(request: TimeRequest, responseObserver: StreamObserver[TimeResponse]): Unit = 31 | scheduler.scheduleWithFixedDelay(0.seconds, 3.seconds) { 32 | responseObserver.onNext(TimeResponse(System.currentTimeMillis())) 33 | } 34 | } 35 | 36 | def main(args: Array[String]): Unit = { 37 | val ssd = ClockGrpc.bindService(new ClockService(), Scheduler.global) 38 | runServer(ssd) 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /demo/src/main/scala/io/ontherocks/hellogrpc/SumServer.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Petra Bierleutgeb 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.ontherocks.hellogrpc 18 | 19 | import io.grpc.stub.StreamObserver 20 | import io.ontherocks.hellogrpc.sum.{ SumGrpc, SumRequest, SumResponse } 21 | import monix.execution.atomic.{ Atomic, AtomicInt } 22 | 23 | import scala.concurrent.ExecutionContext 24 | 25 | object SumServer extends GrpcServer { 26 | 27 | class SumService extends SumGrpc.Sum { 28 | def add(responseObserver: StreamObserver[SumResponse]): StreamObserver[SumRequest] = 29 | new StreamObserver[SumRequest] { 30 | val currentSum: AtomicInt = Atomic(0) // beware: stream observer is not thread-safe 31 | def onError(t: Throwable): Unit = println(s"ON_ERROR: $t") 32 | def onCompleted(): Unit = println("ON_COMPLETED") 33 | def onNext(value: SumRequest): Unit = { 34 | println(s"ON_NEXT: adding value ${value.toAdd}") 35 | responseObserver.onNext(SumResponse(currentSum.addAndGet(value.toAdd))) 36 | } 37 | } 38 | } 39 | 40 | def main(args: Array[String]): Unit = { 41 | val ssd = SumGrpc.bindService(new SumService(), ExecutionContext.global) 42 | runServer(ssd) 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /demo/src/main/scala/io/ontherocks/hellogrpc/SumClient.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Petra Bierleutgeb 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.ontherocks.hellogrpc 18 | 19 | import io.grpc.ManagedChannelBuilder 20 | import io.grpc.stub.StreamObserver 21 | import io.ontherocks.hellogrpc.sum.{ SumGrpc, SumRequest, SumResponse } 22 | import monix.execution.Scheduler.{ global => scheduler } 23 | 24 | import scala.concurrent.duration._ 25 | import scala.util.Random 26 | 27 | object SumClient { 28 | 29 | def main(args: Array[String]): Unit = { 30 | val channel = ManagedChannelBuilder.forAddress("localhost", 50051).usePlaintext(true).build 31 | val client = SumGrpc.stub(channel) 32 | 33 | val responseObserver = new StreamObserver[SumResponse] { 34 | def onError(t: Throwable): Unit = println(s"ON_ERROR: $t") 35 | def onCompleted(): Unit = println("ON_COMPLETED") 36 | def onNext(value: SumResponse): Unit = 37 | println(s"ON_NEXT: Current sum: ${value.currentResult}") 38 | } 39 | 40 | val requestObserver = client.add(responseObserver) 41 | 42 | scheduler.scheduleWithFixedDelay(0.seconds, 5.seconds) { 43 | val toBeAdded = Random.nextInt(11) 44 | println(s"Adding number: $toBeAdded") 45 | requestObserver.onNext(SumRequest(toBeAdded)) 46 | } 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /demo/src/main/scala/io/ontherocks/hellogrpc/HelloWorldClient.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Petra Bierleutgeb 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.ontherocks.hellogrpc 18 | 19 | import io.grpc.ManagedChannelBuilder 20 | import io.ontherocks.hellogrpc.helloworld.HelloWorldGrpc.{ HelloWorldBlockingStub, HelloWorldStub } 21 | import io.ontherocks.hellogrpc.helloworld.ToBeGreeted.Person 22 | import io.ontherocks.hellogrpc.helloworld.{ Greeting, HelloWorldGrpc, ToBeGreeted } 23 | 24 | import scala.concurrent.ExecutionContext.Implicits.global 25 | import scala.concurrent.Future 26 | 27 | object HelloWorldClient { 28 | 29 | def main(args: Array[String]): Unit = { 30 | val channel = ManagedChannelBuilder 31 | .forAddress("localhost", 50051) // host and port of service 32 | .usePlaintext(true) // don't use encryption (for demo purposes) 33 | .build 34 | 35 | val person = Person( 36 | name = "Bob" 37 | ) 38 | 39 | val toBeGreeted = ToBeGreeted(Some(person)) 40 | // or use the generated builder methods 41 | // val toBeGreeted2 = ToBeGreeted().withPerson(Person(name = "Bob")) 42 | 43 | // async client 44 | val stub: HelloWorldStub = HelloWorldGrpc.stub(channel) 45 | val greetingF: Future[Greeting] = stub.sayHello(toBeGreeted) 46 | 47 | greetingF.foreach(response => println(s"ASYNC RESULT: ${response.message}")) 48 | 49 | // beware: blocking code below 50 | val blockingStub: HelloWorldBlockingStub = HelloWorldGrpc.blockingStub(channel) 51 | val greeting: Greeting = blockingStub.sayHello(toBeGreeted) 52 | 53 | println(s"SYNC(BLOCKING) RESULT: ${greeting.message}") 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /demo/src/main/scala/io/ontherocks/hellogrpc/ClockClient.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Petra Bierleutgeb 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.ontherocks.hellogrpc 18 | 19 | import java.util.concurrent.TimeUnit 20 | 21 | import io.grpc.{ ManagedChannel, ManagedChannelBuilder } 22 | import io.grpc.stub.StreamObserver 23 | import io.ontherocks.hellogrpc.clock.{ ClockGrpc, TimeRequest, TimeResponse } 24 | 25 | import scala.concurrent.{ Await, Promise } 26 | import scala.concurrent.duration._ 27 | import scala.util.{ Failure, Success, Try } 28 | 29 | object ClockClient { 30 | 31 | def main(args: Array[String]): Unit = { 32 | val channel = ManagedChannelBuilder.forAddress("localhost", 50051).usePlaintext(true).build 33 | val client = ClockGrpc.stub(channel) 34 | 35 | def observer(p: Promise[String]): StreamObserver[TimeResponse] = 36 | new StreamObserver[TimeResponse] { 37 | def onError(t: Throwable): Unit = { 38 | println(s"ON_ERROR: $t") 39 | p.complete(tryAwaitTermination(channel, "received onError")) 40 | } 41 | def onCompleted(): Unit = { 42 | println("ON_COMPLETED") 43 | p.complete(tryAwaitTermination(channel, "received onComplete")) 44 | } 45 | def onNext(response: TimeResponse): Unit = 46 | println(s"ON_NEXT: Received current time ms: ${response.currentTime}") 47 | } 48 | 49 | val terminated = Promise[String] 50 | client.streamTime(TimeRequest(), observer(terminated)) 51 | 52 | val shutdownReason = Await.result(terminated.future, Duration.Inf) 53 | println(s"Successfully shutdown application. Reason: $shutdownReason") 54 | } 55 | 56 | private def tryAwaitTermination(channel: ManagedChannel, reason: String): Try[String] = { 57 | channel.shutdown() 58 | if (channel.awaitTermination(5, TimeUnit.SECONDS)) { 59 | Success(reason) 60 | } else { 61 | Failure(new RuntimeException("Could not terminate channel.")) 62 | } 63 | } 64 | 65 | } 66 | --------------------------------------------------------------------------------