I have this route:
val route = pathPrefix("es") {
path("se") {
post {
entity(as[JsValue]) {
t =>
complete("ok")
}
}
} ~ path("q" / "show") {
get {
complete(q)
}
}
}
When I try to bind it in order to stop it (according to https://doc.akka.io/docs/akka-http/current/routing-dsl/index.html), I get a compilation error:
val bindingFuture = Http().bindAndHandle(route, "0.0.0.0", 9100)
Error:(54, 46) type mismatch; found : spray.routing.Route (which expands to) spray.routing.RequestContext => Unit required: akka.stream.scaladsl.Flow[akka.http.scaladsl.model.HttpRequest,akka.http.scaladsl.model.HttpResponse,Any] val bindingFuture = Http().bindAndHandle(route, "0.0.0.0", 9100)
How can I stop the HTTP server? Currently I'm able to start the HTTP server with:
startServer("0.0.0.0", port)
However, I don't see how to stop it with the startServer
function.
UPDATE: I cannot upgrade from Spray to Akka HTTP as suggested below (administrative, not in my control).
Looking at Http().bindAndHandle
, it's coming from akka-http-core_2.11-2.4.11.1.jar
. I saw here that I need a RouteResult
to convert it to a Flow
. But I cannot find any RouteResult
in akka-http-core_2.11-2.4.11.1.jar
.
As the other answers have already indicated, you're conflating Spray and Akka HTTP. The two libraries are distinct and their respective server-side components are not meant to coexist in the same application. If you're unable to migrate to Akka HTTP, which supersedes Spray, then remove the Akka HTTP dependencies from your project and look to the Spray documentation for information on stopping a Spray server:
To explicitly stop the server, send an
Http.Unbind
command to theHttpListener
instance (theActorRef
for this instance is available as the sender of theHttp.Bound
confirmation event from when the server was started).The listener will reply with an
Http.Unbound
event after successfully unbinding from the port (or with anHttp.CommandFailed
in the case of error). At that point no further requests will be accepted by the server.
Apparently you're using SimpleRoutingApp
, which is where the startServer
method is defined. This method doesn't expose a way to obtain a reference to the HttpListener
actor. As the quoted documentation states, you have to send an Http.Unbind
message to this actor in order to stop the server.
One idea is to define your own actor that can send a reference to the HttpListener
:
import akka.actor._
import spray.can.Http
import spray.routing._
object MyActor {
case object GetListener
def props(route: => Route): Props = Props(new MyActor(route))
}
class MyActor(route: => Route) extends HttpServiceActor {
import MyActor._
var httpListener: Option[ActorRef] = None
def routeReceive: Receive = runRoute(route)
def serverLifecycleReceive: Receive = {
case b: Http.Bound =>
println(s"Successfully bound to ${b.localAddress}")
val listener = sender()
httpListener = Some(listener)
case GetListener =>
httpListener.foreach(sender ! _)
}
def receive = routeReceive orElse serverLifecycleReceive
}
Then use this actor instead of SimpleRoutingApp
to start the server:
import scala.concurrent.Future
import scala.concurrent.duration._
import scala.util.Success
import akka.actor._
import akka.io.IO
import akka.pattern.ask
import akka.util.Timeout
import spray.can.Http
import spray.http._
import spray.routing._
import MyActor
object Main extends App {
implicit val system = ActorSystem()
import system.dispatcher
implicit val timeout = Timeout(5.seconds)
val route = ???
val handler = system.actorOf(MyActor.props(route), name = "handler")
IO(Http) ! Http.Bind(handler, interface = "0.0.0.0", port = 9100)
// run the below code to shut down the server before shutting down the actor system
(handler ? MyActor.GetListener)
.flatMap { case actor: ActorRef => (actor ? Http.Unbind) }
.onComplete {
case Success(u: Http.Unbound) =>
println("Unbinding from the port is done.")
// system.shutdown()
case _ =>
println("Unbinding failed.")
}
}
All of this assumes that you want to explicitly shut down the server before (or without) shutting down the actor system. If this is not the case, you could of course just shut down the actor system without explicitly stopping the server. For example, you could add a path to your route that handles this (the below code is adapted from one of the sample applications in the Spray repository):
object Main extends App with SimpleRoutingApp {
implicit val system = ActorSystem("simple-routing-app")
import system.dispatcher
val route = ...
~ (post | parameter('method ! "post")) {
path("stop") {
complete {
system.scheduler.scheduleOnce(1.second)(system.shutdown())(system.dispatcher)
"Shutting down in 1 second..."
}
}
}
startServer("0.0.0.0", port = 9100) {
route
}.onComplete {
case Success(b) =>
println(s"Successfully bound to ${b.localAddress}")
case Failure(ex) =>
println(ex.getMessage)
system.shutdown()
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With