Scala integration

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:

build.sbt
libraryDependencies += "com.linecorp.armeria" %% "armeria-scala" % "1.23.1"

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) // πŸ‘ˆ
  ...