Scala integration
Table of contents
As a matter of fact, there are three distinct ways to integrate Armeria and Scala:
- by using the
armeria-scala
library; - by using the
http4s-armeria
library; - or just by using Armeria directly in your Scala projects. In that case,
the supplied API would be fully in
Java
.
If you're searching for the most Scala-way experience of using Armeria,
take a look at armeria-scala
and http4s-armeria
. These two libraries significantly
differentiate from each other.
armeria-scala
is built on top of the Scala Standard Library, and asynchronous computations are provided by thescala.concurrent
library. By picking this library, you will work with HTTP through the Armeria API and get the handy functionality to bridge Java API to Scala.http4s-armeria
, in turn, is built on top of the Functional Programming Libraries āhttp4s
exposes typeful, functional, streaming HTTP API, andCats-Effect
accords a reach functionality for the asynchronous and concurrent computations. This means that Armeria is used as the underlying Backend for the Server and Client.
Since the http4s-armeria
is a third-party project,
its documentation is not listed here, so discover it on their resources. Below on this page
is the documentation of the armeria-scala
library. Choose the most fittable way to integrate
Armeria and Scala according to your demands and needs!
armeria-scala
The com.linecorp.armeria.scala
package provides various useful extension methods and implicit conversions
for an Armeria application written in Scala. To enable it, you first need
the armeria-scala_2.12
or armeria-scala_2.13
dependency:
libraryDependencies += "com.linecorp.armeria" %% "armeria-scala" % "1.31.3"
and then import com.linecorp.armeria.scala.implicits._
in your Scala code:
import com.linecorp.armeria.scala.implicits._
Conversion between Java CompletionStage
and Scala Future
You can convert a Java CompletionStage
to a Scala Future
using the toScala
method:
import com.linecorp.armeria.common.HttpResponse
import com.linecorp.armeria.scala.implicits._
import com.linecorp.armeria.server.Server
import java.util.concurrent.CompletableFuture
import scala.concurrent.Future
val server =
Server
.builder()
.service("/", (ctx, req) => HttpResponse.of(200))
.build()
val javaFuture: CompletableFuture[Void] = server.start()
val scalaFuture: Future[Unit] = javaFuture.toScala // š
You can also convert a Scala Future
to a Java CompletionStage
using the toJava
method:
import java.util.concurrent.CompletionStage
val javaFuture: CompletionStage[Void] = scalaFuture.toJava // š
You'll also find the extension method toHttpResponse
added to Future[HttpResponse]
and
CompletionStage[HttpResponse]
when converting an asynchronous result into an HttpResponse
:
import com.linecorp.armeria.common.MediaType
import com.linecorp.armeria.scala.implicits._
val future: Future[String] = ...
val response =
future
.map { value => HttpResponse.of(MediaType.PLAIN_TEXT_UTF_8, value) }
.toHttpResponse // š
ExecutionContexts.sameThread
In an asynchronous system that the entire application logic runs on event loops, it is often useful
in terms of performance to invoke the callbacks attached to a Future
directly rather than submitting
the callbacks to another ExecutionContext
. You can use ExecutionContexts.sameThread
in such a case:
import com.linecorp.armeria.common.HttpRequest
import com.linecorp.armeria.scala.ExecutionContexts.sameThread
import com.linecorp.armeria.scala.implicits._
import com.linecorp.armeria.server.HttpService
import com.linecorp.armeria.server.ServiceRequestContext
import scala.concurrent.ExecutionContext
class MyHttpService extends HttpService {
override def serve(ctx: ServiceRequestContext, req: HttpRequest): HttpResponse = {
implicit val ec: ExecutionContext = sameThread // š
// Perform some asynchronous operation.
val future: Future[String] = ...
// Convert the result to a response.
future
.map { value =>
HttpResponse.of(MediaType.PLAIN_TEXT_UTF_8, value)
}
.toHttpResponse
}
}
Context-aware ExecutionContext
You can use the eventLoopExecutionContext
extension method in RequestContext
to specify a Scala
ExecutionContext
that submits all tasks to the current context's event loop thread:
import com.linecorp.armeria.scala.implicits._
val ctx = ServiceRequestContext.current
implicit val ec: ExecutionContext = ctx.eventLoopExecutionContext // š
Future {
// Do some non-blocking job here.
}
For long-running tasks running on the server side, you can use blockingTaskExecutionContext
:
import com.linecorp.armeria.scala.implicits._
val ctx = ServiceRequestContext.current
implicit val ec: ExecutionContext = ctx.blockingTaskExecutionContext // š
Future {
Thread.sleep(1000)
}
Collection converters
com.linecorp.armeria.scala.implicits._
will add toScala
and toJava
conversion extension methods to
Java collections and Scala collections respectively. It means you don't need to import
scala.jdk.CollectionConverters._
if you imported Armeria's implicits.
import com.linecorp.armeria.scala.implicits._
import com.linecorp.armeria.server.Server
val server: Server = ...
val scalaList = server.activePorts.toScala // š
Implicit conversion between Java Duration
and Scala FiniteDuration
A Scala FiniteDuration
is implicitly converted into a Java Duration
and vice versa for your convenience:
import com.linecorp.armeria.scala.implicits._
import scala.concurrent.duration._
Server
.builder()
.requestTimeout(5.seconds) // š
...