scalacenter / bloop

Bloop is a build server and CLI tool to compile, test and run Scala fast from any editor or build tool.
https://scalacenter.github.io/bloop/
Apache License 2.0
896 stars 203 forks source link

Bloop `run --watch` doesn't appear to reload when Spring Boot "Spring-Fu" functional routing Netty app source changes #1524

Open GavinRay97 opened 3 years ago

GavinRay97 commented 3 years ago

I have a Scala 3 app with the buildfile in SBT, using the above mentioned setup.

Doing bloop run <appname> --watch doesn't appear to reload it on changes. Is there a trick to getting this to work 👀

https://user-images.githubusercontent.com/26604994/123532152-bb626580-d6d8-11eb-94e3-281a3516b5c2.mp4

package org.example

import scala.beans.BeanProperty
import reactor.core.publisher.Mono
import org.springframework.fu.jafu.Jafu.reactiveWebApplication
import org.springframework.fu.jafu.webflux.WebFluxServerDsl.webFlux
import org.springframework.web.reactive.function.server.ServerRequest
import org.springframework.web.reactive.function.server.ServerResponse
import org.springframework.web.reactive.function.server.ServerResponse.ok
import org.springframework.fu.jafu.webflux.WebFluxServerDsl

case class Sample(@BeanProperty message: String)

class SampleService:
  def generateMessage(): String = "Hello world!"

class SampleHandler(private var sampleService: SampleService):

  def hello(request: ServerRequest): Mono[ServerResponse] =
    ServerResponse.ok().bodyValue(sampleService.generateMessage())

  def json(request: ServerRequest): Mono[ServerResponse] =
    ServerResponse.ok().bodyValue(new Sample(sampleService.generateMessage()))

object Application {
  val app = reactiveWebApplication((a) =>
    a.beans((b) => b.bean(classOf[SampleHandler]).bean(classOf[SampleService]))
      .enable(
        webFlux((server) =>
          server
            .port(if (server.profiles().contains("test")) 8181 else 8080)
            .router((app) => {
              val handler = server.ref(classOf[SampleHandler])
              app.GET("/", handler.hello)
              app.GET("/api", handler.json)
            })
            .codecs(_.string().jackson())
        )
      )
  )

  def main(args: Array[String]): Unit = app.run(args)
}
ThisBuild / organization := "com.example"
ThisBuild / version := "0.0.1-SNAPSHOT"
ThisBuild / scalaVersion := "3.0.0"

val SPRING_VERSION = "2.6.0-SNAPSHOT"

lazy val root = (project in file("."))
  .settings(
    name := "demo",
    resolvers ++= List(
      "Spring Milestones" at "https://repo.spring.io/milestone",
      "Spring Snapshots" at "https://repo.spring.io/snapshot"
    ),
    libraryDependencies ++= List(
      "org.springframework.boot" % "spring-boot-starter-parent" % SPRING_VERSION pomOnly (),
      "org.springframework.boot" % "spring-boot-starter-actuator" % SPRING_VERSION,
      "org.springframework.boot" % "spring-boot-starter-webflux" % SPRING_VERSION,
      "org.springframework.fu" % "spring-fu-jafu" % "0.4.5-SNAPSHOT",
      "org.springframework.boot" % "spring-boot-starter-test" % SPRING_VERSION % Test,
      "io.projectreactor" % "reactor-test" % "3.4.7" % Test
    )
  )

bloopMainClass in (Compile, run) := Some("org.example.Application")

lazy val props = new {
  val javaVersion = "16"
}

lazy val libs = new {}
tgodzik commented 3 years ago

Thanks for reporting! Did this work with an equivalent project in Scala 2? Or is it an overall global issue? Not sure yet what might be exactly a reason, but will take a look at it at some point.

GavinRay97 commented 3 years ago

I haven't yet tried it with Scala 2 (just started looking into Scala in general a few days ago, so still getting acquainted with the ecosystem/tooling).

Not sure yet what might be exactly a reason, but will take a look at it at some point.

Does bloop wait for a process to finish before reloading? It could be that. After I searched more on this, I found that the below two solutions both worked:

# With watchexec binary: https://github.com/watchexec/watchexec
$ watchexec --exts "scala" --watch ./src/main/scala --restart -- bloop run root

# With sbt-revolver plugin: https://github.com/spray/sbt-revolver
$ sbt ~reStart

The trick with watchexec and it's cousin entr http://eradman.com/entrproject/ are that they have a flag -r/--reload for long-running processes like webservers where they first stop/kill the current PID before restarting it again. I think this is how the sbt-revolver plugin works as well, but I didn't look too much into it.

image

tgodzik commented 3 years ago

--watch should work here from what I understand, but I might know the full story here.

kubukoz commented 3 years ago

I tried --watch a couple times with bloop and it never stops the application. The app neeeds to quit on its own, only then will the file watching trigger a re-run.

This, in general, could be improved but seems like a bigger project :) e.g. the semantics of how you kill the previous instances would have to be defined (sigint, then sigkill after a timeout?)

tgodzik commented 3 years ago

I tried --watch a couple times with bloop and it never stops the application. The app neeeds to quit on its own, only then will the file watching trigger a re-run.

This, in general, could be improved but seems like a bigger project :) e.g. the semantics of how you kill the previous instances would have to be defined (sigint, then sigkill after a timeout?)

Ach, yeah you are right! Something like hot swapping would most likely be useful here, but I haven't yet got back to that PR

ches commented 2 years ago

I believe this boils down to a duplicate of #558