Skip to content

Commit f9b88bc

Browse files
committed
unused import
1 parent 3e559a0 commit f9b88bc

File tree

14 files changed

+379
-7
lines changed

14 files changed

+379
-7
lines changed
+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name := "akka-grpc-quickstart-scala"
2+
3+
version := "1.0"
4+
5+
scalaVersion := "2.13.12"
6+
7+
lazy val akkaVersion = sys.props.getOrElse("akka.version", "2.9.1")
8+
lazy val akkaGrpcVersion = sys.props.getOrElse("akka.grpc.version", "2.4.0")
9+
10+
enablePlugins(AkkaGrpcPlugin)
11+
12+
// Run in a separate JVM, to make sure sbt waits until all threads have
13+
// finished before returning.
14+
// If you want to keep the application running while executing other
15+
// sbt tasks, consider https://github.com/spray/sbt-revolver/
16+
fork := true
17+
18+
resolvers += "Akka library repository".at("https://repo.akka.io/maven")
19+
20+
// GraalVM native image build
21+
enablePlugins(NativeImagePlugin)
22+
nativeImageJvm := "graalvm-community"
23+
nativeImageVersion := "21.0.2"
24+
nativeImageOptions := Seq("--no-fallback", "--verbose")
25+
26+
libraryDependencies ++= Seq(
27+
"com.typesafe.akka" %% "akka-actor-typed" % akkaVersion,
28+
"com.typesafe.akka" %% "akka-stream" % akkaVersion,
29+
"com.typesafe.akka" %% "akka-discovery" % akkaVersion,
30+
"com.typesafe.akka" %% "akka-pki" % akkaVersion,
31+
"ch.qos.logback" % "logback-classic" % "1.2.3",
32+
"com.typesafe.akka" %% "akka-actor-testkit-typed" % akkaVersion % Test,
33+
"com.typesafe.akka" %% "akka-stream-testkit" % akkaVersion % Test,
34+
"org.scalatest" %% "scalatest" % "3.2.12" % Test)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
sbt.version=1.9.8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
resolvers += "Akka library repository".at("https://repo.akka.io/maven")
2+
3+
addSbtPlugin("com.lightbend.akka.grpc" % "sbt-akka-grpc" % "2.4.0")
4+
addSbtPlugin("org.scalameta" % "sbt-native-image" % "0.3.4")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
public class Empty {
2+
// Just an empty class
3+
// because gradle complained about not having any Java files to compile?
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//#service-request-reply
2+
syntax = "proto3";
3+
4+
option java_multiple_files = true;
5+
option java_package = "com.example.helloworld";
6+
option java_outer_classname = "HelloWorldProto";
7+
8+
// The greeting service definition.
9+
service GreeterService {
10+
// Sends a greeting
11+
rpc SayHello (HelloRequest) returns (HelloReply) {}
12+
//#service-request-reply
13+
//#service-stream
14+
// The stream of incoming HelloRequest messages are
15+
// sent out as corresponding HelloReply. From
16+
// all clients to all clients, like a chat room.
17+
rpc SayHelloToAll (stream HelloRequest) returns (stream HelloReply) {}
18+
//#service-stream
19+
//#service-request-reply
20+
}
21+
22+
// The request message containing the user's name.
23+
message HelloRequest {
24+
string name = 1;
25+
}
26+
27+
// The response message containing the greetings
28+
message HelloReply {
29+
string message = 1;
30+
}
31+
//#service-request-reply
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
akka.grpc.client {
2+
"helloworld.GreeterService" {
3+
host = 127.0.0.1
4+
port = 8080
5+
override-authority = foo.test.google.fr
6+
trusted = /certs/ca.pem
7+
}
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIICSjCCAbOgAwIBAgIJAJHGGR4dGioHMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV
3+
BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
4+
aWRnaXRzIFB0eSBMdGQxDzANBgNVBAMTBnRlc3RjYTAeFw0xNDExMTEyMjMxMjla
5+
Fw0yNDExMDgyMjMxMjlaMFYxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0
6+
YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxDzANBgNVBAMT
7+
BnRlc3RjYTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwEDfBV5MYdlHVHJ7
8+
+L4nxrZy7mBfAVXpOc5vMYztssUI7mL2/iYujiIXM+weZYNTEpLdjyJdu7R5gGUu
9+
g1jSVK/EPHfc74O7AyZU34PNIP4Sh33N+/A5YexrNgJlPY+E3GdVYi4ldWJjgkAd
10+
Qah2PH5ACLrIIC6tRka9hcaBlIECAwEAAaMgMB4wDAYDVR0TBAUwAwEB/zAOBgNV
11+
HQ8BAf8EBAMCAgQwDQYJKoZIhvcNAQELBQADgYEAHzC7jdYlzAVmddi/gdAeKPau
12+
sPBG/C2HCWqHzpCUHcKuvMzDVkY/MP2o6JIW2DBbY64bO/FceExhjcykgaYtCH/m
13+
oIU63+CFOTtR7otyQAWHqXa7q4SbCDlG7DyRFxqG0txPtGvy12lgldA2+RgcigQG
14+
Dfcog5wrJytaQ6UA0wE=
15+
-----END CERTIFICATE-----
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
-----BEGIN PRIVATE KEY-----
2+
MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAOHDFScoLCVJpYDD
3+
M4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlumN+fm+AjPEK5GHhGn1BgzkWF+slf
4+
3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wHNVX77fBZOgp9VlSMVfyd9N8YwbBY
5+
AckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAECgYAn7qGnM2vbjJNBm0VZCkOkTIWm
6+
V10okw7EPJrdL2mkre9NasghNXbE1y5zDshx5Nt3KsazKOxTT8d0Jwh/3KbaN+YY
7+
tTCbKGW0pXDRBhwUHRcuRzScjli8Rih5UOCiZkhefUTcRb6xIhZJuQy71tjaSy0p
8+
dHZRmYyBYO2YEQ8xoQJBAPrJPhMBkzmEYFtyIEqAxQ/o/A6E+E4w8i+KM7nQCK7q
9+
K4JXzyXVAjLfyBZWHGM2uro/fjqPggGD6QH1qXCkI4MCQQDmdKeb2TrKRh5BY1LR
10+
81aJGKcJ2XbcDu6wMZK4oqWbTX2KiYn9GB0woM6nSr/Y6iy1u145YzYxEV/iMwff
11+
DJULAkB8B2MnyzOg0pNFJqBJuH29bKCcHa8gHJzqXhNO5lAlEbMK95p/P2Wi+4Hd
12+
aiEIAF1BF326QJcvYKmwSmrORp85AkAlSNxRJ50OWrfMZnBgzVjDx3xG6KsFQVk2
13+
ol6VhqL6dFgKUORFUWBvnKSyhjJxurlPEahV6oo6+A+mPhFY8eUvAkAZQyTdupP3
14+
XEFQKctGz+9+gKkemDp7LBBMEMBXrGTLPhpEfcjv/7KPdnFHYmhYeBTBnuVmTVWe
15+
F98XJ7tIFfJq
16+
-----END PRIVATE KEY-----
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIICnDCCAgWgAwIBAgIBBzANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJBVTET
3+
MBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQ
4+
dHkgTHRkMQ8wDQYDVQQDEwZ0ZXN0Y2EwHhcNMTUxMTA0MDIyMDI0WhcNMjUxMTAx
5+
MDIyMDI0WjBlMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNV
6+
BAcTB0NoaWNhZ28xFTATBgNVBAoTDEV4YW1wbGUsIENvLjEaMBgGA1UEAxQRKi50
7+
ZXN0Lmdvb2dsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOHDFSco
8+
LCVJpYDDM4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlumN+fm+AjPEK5GHhGn1Bg
9+
zkWF+slf3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wHNVX77fBZOgp9VlSMVfyd
10+
9N8YwbBYAckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAGjazBpMAkGA1UdEwQCMAAw
11+
CwYDVR0PBAQDAgXgME8GA1UdEQRIMEaCECoudGVzdC5nb29nbGUuZnKCGHdhdGVy
12+
em9vaS50ZXN0Lmdvb2dsZS5iZYISKi50ZXN0LnlvdXR1YmUuY29thwTAqAEDMA0G
13+
CSqGSIb3DQEBCwUAA4GBAJFXVifQNub1LUP4JlnX5lXNlo8FxZ2a12AFQs+bzoJ6
14+
hM044EDjqyxUqSbVePK0ni3w1fHQB5rY9yYC5f8G7aqqTY1QOhoUk8ZTSTRpnkTh
15+
y4jjdvTZeLDVBlueZUTDRmy2feY5aZIU18vFDK08dTG0A87pppuv1LNIR3loveU8
16+
-----END CERTIFICATE-----
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<configuration>
3+
<!-- This is a development logging configuration that logs to standard out, for an example of a production
4+
logging config, see the Akka docs: https://doc.akka.io/docs/akka/2.6/typed/logging.html#logback -->
5+
<appender name="STDOUT" target="System.out" class="ch.qos.logback.core.ConsoleAppender">
6+
<encoder>
7+
<pattern>[%date{ISO8601}] [%level] [%logger] [%thread] [%X{akkaSource}] - %msg%n</pattern>
8+
</encoder>
9+
</appender>
10+
11+
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
12+
<queueSize>1024</queueSize>
13+
<neverBlock>true</neverBlock>
14+
<appender-ref ref="STDOUT" />
15+
</appender>
16+
17+
<root level="INFO">
18+
<appender-ref ref="ASYNC"/>
19+
</root>
20+
21+
</configuration>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package com.example.helloworld
2+
3+
//#import
4+
import scala.concurrent.duration._
5+
import scala.concurrent.{ExecutionContext, Future}
6+
import scala.util.Failure
7+
import scala.util.Success
8+
import akka.Done
9+
import akka.NotUsed
10+
import akka.actor.typed.ActorSystem
11+
import akka.actor.typed.scaladsl.Behaviors
12+
import akka.grpc.GrpcClientSettings
13+
import akka.stream.scaladsl.Source
14+
15+
//#import
16+
17+
//#client-request-reply
18+
object GreeterClient {
19+
20+
def main(args: Array[String]): Unit = {
21+
implicit val sys: ActorSystem[Nothing] = ActorSystem[Nothing](Behaviors.empty[Nothing], "GreeterClient")
22+
implicit val ec: ExecutionContext = sys.executionContext
23+
24+
val client = GreeterServiceClient(GrpcClientSettings.fromConfig("helloworld.GreeterService"))
25+
26+
val names =
27+
if (args.isEmpty) List("Alice", "Bob")
28+
else args.toList
29+
30+
names.foreach(singleRequestReply)
31+
32+
//#client-request-reply
33+
if (args.nonEmpty)
34+
names.foreach(streamingBroadcast)
35+
//#client-request-reply
36+
37+
def singleRequestReply(name: String): Unit = {
38+
println(s"Performing request: $name")
39+
val reply = client.sayHello(HelloRequest(name))
40+
reply.onComplete {
41+
case Success(msg) =>
42+
println(msg)
43+
case Failure(e) =>
44+
println(s"Error: $e")
45+
}
46+
}
47+
48+
//#client-request-reply
49+
//#client-stream
50+
def streamingBroadcast(name: String): Unit = {
51+
println(s"Performing streaming requests: $name")
52+
53+
val requestStream: Source[HelloRequest, NotUsed] =
54+
Source
55+
.tick(1.second, 1.second, "tick")
56+
.zipWithIndex
57+
.map { case (_, i) => i }
58+
.map(i => HelloRequest(s"$name-$i"))
59+
.mapMaterializedValue(_ => NotUsed)
60+
61+
val responseStream: Source[HelloReply, NotUsed] = client.sayHelloToAll(requestStream)
62+
val done: Future[Done] =
63+
responseStream.runForeach(reply => println(s"$name got streaming reply: ${reply.message}"))
64+
65+
done.onComplete {
66+
case Success(_) =>
67+
println("streamingBroadcast done")
68+
case Failure(e) =>
69+
println(s"Error streamingBroadcast: $e")
70+
}
71+
}
72+
//#client-stream
73+
//#client-request-reply
74+
75+
}
76+
77+
}
78+
//#client-request-reply
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package com.example.helloworld
2+
3+
//#import
4+
5+
6+
import java.security.KeyStore
7+
import java.security.SecureRandom
8+
import java.security.cert.Certificate
9+
import java.security.cert.CertificateFactory
10+
11+
import scala.io.Source
12+
13+
import akka.actor.typed.ActorSystem
14+
import akka.actor.typed.scaladsl.Behaviors
15+
import akka.http.scaladsl.ConnectionContext
16+
import akka.http.scaladsl.Http
17+
import akka.http.scaladsl.HttpsConnectionContext
18+
import akka.http.scaladsl.model.HttpRequest
19+
import akka.http.scaladsl.model.HttpResponse
20+
import akka.pki.pem.DERPrivateKeyLoader
21+
import akka.pki.pem.PEMDecoder
22+
import com.typesafe.config.ConfigFactory
23+
import javax.net.ssl.KeyManagerFactory
24+
import javax.net.ssl.SSLContext
25+
26+
import scala.concurrent.ExecutionContext
27+
import scala.concurrent.Future
28+
import scala.util.Failure
29+
import scala.util.Success
30+
import scala.concurrent.duration._
31+
//#import
32+
33+
34+
//#server
35+
object GreeterServer {
36+
37+
def main(args: Array[String]): Unit = {
38+
// important to enable HTTP/2 in ActorSystem's config
39+
val conf = ConfigFactory.parseString("akka.http.server.enable-http2 = on")
40+
.withFallback(ConfigFactory.defaultApplication())
41+
val system = ActorSystem[Nothing](Behaviors.empty[Nothing], "GreeterServer", conf)
42+
new GreeterServer(system).run()
43+
}
44+
}
45+
46+
class GreeterServer(system: ActorSystem[_]) {
47+
48+
def run(): Future[Http.ServerBinding] = {
49+
implicit val sys = system
50+
implicit val ec: ExecutionContext = system.executionContext
51+
52+
val service: HttpRequest => Future[HttpResponse] =
53+
GreeterServiceHandler(new GreeterServiceImpl(system))
54+
55+
val bound: Future[Http.ServerBinding] = Http()(system)
56+
.newServerAt(interface = "127.0.0.1", port = 8080)
57+
.enableHttps(serverHttpContext)
58+
.bind(service)
59+
.map(_.addToCoordinatedShutdown(hardTerminationDeadline = 10.seconds))
60+
61+
bound.onComplete {
62+
case Success(binding) =>
63+
val address = binding.localAddress
64+
println(s"gRPC server bound to ${address.getHostString}:${address.getPort}")
65+
case Failure(ex) =>
66+
println("Failed to bind gRPC endpoint, terminating system")
67+
ex.printStackTrace()
68+
system.terminate()
69+
}
70+
71+
bound
72+
}
73+
//#server
74+
75+
76+
private def serverHttpContext: HttpsConnectionContext = {
77+
val privateKey =
78+
DERPrivateKeyLoader.load(PEMDecoder.decode(readPrivateKeyPem()))
79+
val fact = CertificateFactory.getInstance("X.509")
80+
val cer = fact.generateCertificate(
81+
classOf[GreeterServer].getResourceAsStream("/certs/server1.pem")
82+
)
83+
val ks = KeyStore.getInstance("PKCS12")
84+
ks.load(null)
85+
ks.setKeyEntry(
86+
"private",
87+
privateKey,
88+
new Array[Char](0),
89+
Array[Certificate](cer)
90+
)
91+
val keyManagerFactory = KeyManagerFactory.getInstance("SunX509")
92+
keyManagerFactory.init(ks, null)
93+
val context = SSLContext.getInstance("TLS")
94+
context.init(keyManagerFactory.getKeyManagers, null, new SecureRandom)
95+
ConnectionContext.httpsServer(context)
96+
}
97+
98+
private def readPrivateKeyPem(): String =
99+
Source.fromResource("certs/server1.key").mkString
100+
//#server
101+
102+
}
103+
//#server
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.example.helloworld
2+
3+
//#import
4+
import scala.concurrent.Future
5+
6+
import akka.NotUsed
7+
import akka.actor.typed.ActorSystem
8+
import akka.stream.scaladsl.BroadcastHub
9+
import akka.stream.scaladsl.Keep
10+
import akka.stream.scaladsl.MergeHub
11+
import akka.stream.scaladsl.Sink
12+
import akka.stream.scaladsl.Source
13+
14+
//#import
15+
16+
//#service-request-reply
17+
//#service-stream
18+
class GreeterServiceImpl(system: ActorSystem[_]) extends GreeterService {
19+
private implicit val sys: ActorSystem[_] = system
20+
21+
//#service-request-reply
22+
val (inboundHub: Sink[HelloRequest, NotUsed], outboundHub: Source[HelloReply, NotUsed]) =
23+
MergeHub.source[HelloRequest]
24+
.map(request => HelloReply(s"Hello, ${request.name}"))
25+
.toMat(BroadcastHub.sink[HelloReply])(Keep.both)
26+
.run()
27+
//#service-request-reply
28+
29+
override def sayHello(request: HelloRequest): Future[HelloReply] = {
30+
Future.successful(HelloReply(s"Hello, ${request.name}"))
31+
}
32+
33+
//#service-request-reply
34+
override def sayHelloToAll(in: Source[HelloRequest, NotUsed]): Source[HelloReply, NotUsed] = {
35+
in.runWith(inboundHub)
36+
outboundHub
37+
}
38+
//#service-request-reply
39+
}
40+
//#service-stream
41+
//#service-request-reply

0 commit comments

Comments
 (0)