Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I perform an action on server startup in the Scala Play Framework?

I have a config file servers.conf in my conf/ directory that is read by my ServerController whenever the route /servers is hit. This isn't performant because it requires a re-read of the configuration file on each successive hit when the file won't change. Further if there are problems with the config file, I can tell the user ASAP rather than throw an exception on a page hit.

Currently I have this in my ServerController.scala:

case class Server(ip: String, port: String)

/**
  * This controller creates an `Action` to handle HTTP requests to the
  * application's server page.
  */
@Singleton
class ServerController @Inject() extends Controller {

  /**
    * Create an Action to render an HTML page with a the list of servers.
    * The configuration in the `routes` file means that this method
    * will be called when the application receives a `GET` request with
    * a path of `/servers`.
    */
  def index = Action {

    val serverList = ConfigFactory.load().getConfigList("servers")
    val servers: List[Server] = serverList match {
      case null => Nil
      case _ => serverList map { s =>
        Server(s.getString("ip"), s.getString("port"))
      } filter { s =>
        s.ip != null && s.port != null
      }.toList
    }

    Ok(views.html.servers(servers))
  }
}

My goal is to have the server read the config file at startup and pass the list of servers to the ServerController when the route is hit if there are no problems reading in the config file. If there are problems, I want an exception to be thrown immediately.

I can't seem to find an entry point for my application, though, so I don't know how to perform actions on startup.

Does anyone know how to do this? I'm using Play 2.5.x.

like image 438
erip Avatar asked Apr 06 '16 14:04

erip


People also ask

What is play framework in Scala?

Play Framework makes it easy to build web applications with Java & Scala. Play is based on a lightweight, stateless, web-friendly architecture. Built on Akka, Play provides predictable and minimal resource consumption (CPU, memory, threads) for highly-scalable applications.

What is sbt in play framework?

sbt file defines settings for your project. You can also define your own custom settings for your project, as described in the sbt documentation. In particular, it helps to be familiar with the settings in sbt.

Does Play support Scala 3?

Play Framework does not support Scala 3, as of August 2021. Play depends on lots of other libraries, not all of them support Scala 3 yet. So even if you configure sbt correctly, as per Matthias' answer, apparently you still cannot use Scala 3 yet.


1 Answers

If you're using the latest version of Play, it looks on startup for any class called Module that is in the root package (that is, there is no package definition at the top of the file). Here is an example taken from the latest Activator template for Play 2.5.x, which I have modified for demonstration of running code on application startup and shutdown:

In services/Say.scala, this would be a simple service to say "Hello!" on startup and "Goodbye!" when the application shuts down:

package services

import javax.inject._
import play.api.inject.ApplicationLifecycle
import scala.concurrent.Future

trait Say {
  def hello(): Unit
  def goodbye(): Unit
}

@Singleton
class SayImpl @Inject() (appLifecycle: ApplicationLifecycle) extends Say {  
    override def hello(): Unit = println("Hello!")
    override def goodbye(): Unit = println("Goodbye!")

    // You can do this, or just explicitly call `hello()` at the end
    def start(): Unit = hello()

    // When the application starts, register a stop hook with the
    // ApplicationLifecycle object. The code inside the stop hook will
    // be run when the application stops.
    appLifecycle.addStopHook { () =>
        goodbye()
        Future.successful(())
    }

    // Called when this singleton is constructed (could be replaced by `hello()`)
    start()
}

In Module.scala,

import com.google.inject.AbstractModule
import services._

/**
 * This class is a Guice module that tells Guice how to bind several
 * different types. This Guice module is created when the Play
 * application starts.

 * Play will automatically use any class called `Module` that is in
 * the root package. You can create modules in other locations by
 * adding `play.modules.enabled` settings to the `application.conf`
 * configuration file.
 */
class Module extends AbstractModule {

  override def configure() = {
    // We bind the implementation to the interface (trait) as an eager singleton,
    // which means it is bound immediately when the application starts.
    bind(classOf[Say]).to(classOf[SayImpl]).asEagerSingleton()
  }
}

Some further resources you may find useful are the Scala dependency injection (DI) documentation and the Guice documentation. Guice is the default DI framework used by Play.

like image 119
Eric Avatar answered Oct 18 '22 04:10

Eric