I have a Play framework app in Scala. It uses database and I need to load data into database before first app start. I thought I could add a class with a main class in the app and start it like play -main loadDataClass. It seems to be working but once I access Play.current.configuration I need this to access database credentials. I get this error:
Exception in thread "main" java.lang.RuntimeException: There is no started application
Please advice how to implement this. Should I somehow start the application?
You need to create a module for loading data on start up. In the production mode (activator start) it would run after application start, in the development mode (activator run) it would run after first request to the server.
Create module
app/modules/PrepareDatabaseModule.scala:
inside this module
package modules
import com.google.inject.AbstractModule
trait PrepareDatabase {}
class PrepareDatabaseClass extends PrepareDatabase {
initialize()
def initialize() = {
// Load your data here
}
}
class PrepareDatabaseModule extends AbstractModule {
def configure() = {
bind(classOf[PrepareDatabase])
.to(classOf[PrepareDatabaseClass]).asEagerSingleton
}
}
in conf/application.conf:
play.modules.enabled += "modules.PrepareDatabaseModule"
Related
I have a simple akka Http Server:
import akka.http.scaladsl.model.{ContentTypes, HttpEntity}
import akka.http.scaladsl.server.HttpApp
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.settings.ServerSettings
import com.typesafe.config.ConfigFactory
object MinimalHttpServer extends HttpApp {
def route =
pathPrefix("v1") {
path("subscribe" / Segment) { id =>
get {
complete(HttpEntity(ContentTypes.`text/html(UTF-8)`, s"<h1>Hello $id from Akka Http!</h1>"))
} ~
post {
entity(as[String]) { entity =>
complete(HttpEntity(ContentTypes.`text/html(UTF-8)`, s"<b>Thanks $id for posting your message <i>$entity</i></b>"))
}
}
}
}
}
object MinimalHttpServerApplication extends App {
MinimalHttpServer.startServer("localhost", 8088, ServerSettings(ConfigFactory.load))
}
I use Sbt native Packager to build an universal zip. When I deploy my application to Aws Elastic Beanstalk, I receive this error:
[Instance: i-0a846978718d54d76] Command failed on instance. Return code: 1 Output: (TRUNCATED)...xml_2.11-1.0.5.jar Unable to launch application as the source bundle does not contain either a file named application.jar or a Procfile. Unable to launch application as the source bundle does not contain either a file named application.jar or a Procfile. Hook /opt/elasticbeanstalk/hooks/appdeploy/pre/01_configure_application.sh failed. For more detail, check /var/log/eb-activity.log using console or EB CLI.
Any Ideas? Thank You!
It appears AWS Elastic Beanstalk expects your .zip to contain either a file named application.jar or a Procfile, and the zip created by sbt-native-packager does not look like that.
It appears sbt-native-packager does not have support for the format Elastic Beanstalk expects, though GitHub issue 632 shows some work done in that direction.
I am trying to set the TimeToLive setting for DNS Lookup in my Scala-Play application. I use Play 2.5.9 and Scala 2.11.8 and follow the AWS guide. I tried the following ways:
in application.conf
// Set DNS lookup time-to-live to one minute
networkaddress.cache.ttl=1
networkaddress.cache.negative.ttl=1
in AppModule or EagerSingleton (the code would be similar)
class AppModule() extends AbstractModule {
Security.setProperty("networkaddress.cache.ttl", "1")
Security.setProperty("networkaddress.cache.negative.ttl", "1")
...
}
passing as environment variable:
sbt -Dsun.net.inetaddr.ttl=1 clean run
I have the following piece of test code in the application:
for (i <- 1 to 25) {
System.out.println(java.net.InetAddress.getByName("google.com").getHostAddress())
Thread.sleep(1000)
}
This always prints the same IP address, e.g. 216.58.212.206. To me it looks like none of the approaches specified above have any effect. However, maybe I am testing something else and not actually the value of TTL. Therefore, I have two questions:
what is the correct way to pass a security variable into a Play application?
how to test it?
To change the settings for DNS cache via java.security.Security you have to provide a custom application loader.
package modules
class ApplicationLoader extends GuiceApplicationLoader {
override protected def builder(context: Context): GuiceApplicationBuilder = {
java.security.Security.setProperty("networkaddress.cache.ttl", "1")
super.builder(context)
}
}
When you build this application loader you can enable it in your application.conf
play.application.loader = "modules.ApplicationLoader"
after that you could use your code above and check if the DNS cache is behaving like you set it up. But keep in mind that your system is accessing a DNS server which is caching itself so you wont see change then.
If you want to be sure that you get different addresses for google.com you should use an authority name server like ns1.google.com
If you want to write a test on that you could maybe write a test which requests the address and then waits for the specified amount of time until it resolves again. But with a DNS system out of your control like google.com this could be a problem, if you hit a DNS server with caching.
If you want to write such a check you could do it with
#RunWith(classOf[JUnitRunner])
class DnsTests extends FlatSpec with Matchers {
"DNS Cache ttl" should "refresh after 1 second"
in new WithApplicationLoader(new modules.ApplicationLoader) {
// put your test code here
}
}
As you can see you can put the custom application loader in the context of the application starting behind your test.
I need to read some configuration values just after the configuration file has been loaded but before the application actually starts.
In Play 2.3.x I used to override GlobalSettings.onLoadConfig, which is deprecated in Play 2.4.x. The official documentation says one should use GuiceApplicationBuilder.loadConfig instead.
Again, the documentation is a bit poor and I was unable to find more details or an example... so any help would be really appreciated.
1. Before app starts
If you need to read configuration before app starts, this approach can be used:
modules/CustomApplicationLoader.scala:
package modules
import play.api.ApplicationLoader
import play.api.Configuration
import play.api.inject._
import play.api.inject.guice._
class CustomApplicationLoader extends GuiceApplicationLoader() {
override def builder(context: ApplicationLoader.Context): GuiceApplicationBuilder = {
println(context.initialConfiguration) // <- the configuration
initialBuilder
.in(context.environment)
.loadConfig(context.initialConfiguration)
.overrides(overrides(context): _*)
}
}
conf/application.conf has the following added:
play.application.loader = "modules.CustomApplicationLoader"
With that, I see the following in console (snipped as too long):
Configuration(Config(SimpleConfigObject({"akka":{"actor":{"creation-timeout":"20s"...
Source: documentation.
2. Not before app starts
If you don't need to read configuration before app starts, this approach can be used instead: (it's so embarrassingly simple) the Module bindings method takes a Play Environment and Configuration for you to read:
class HelloModule extends Module {
def bindings(environment: Environment,
configuration: Configuration) = {
println(configuration) // <- the configuration
Seq(
bind[Hello].qualifiedWith("en").to[EnglishHello],
bind[Hello].qualifiedWith("de").to[GermanHello]
)
}
}
Have a somewhat simple project deployed to a JAR. I am starting up a supervisor actor that confirms it is booting up by sending out the following log message:
[akka://service-kernel/user/Tracker] Starting new Tracker
However, when I go to reference the actor via actorFor locally with an sbt run, it finds it no problem. In production, I use the same .actorFor("akka://service-kernel/user/Tracker") and it throws a NullPointerException. I can confirm via the logs that in production, the Tracker has sent out its confirmation that it booted up.
Are there any issues when using a Microkernel deployed to a JAR to make actor references?
Edit
I am suspecting that both the way I reference the system and the way Akka treats the start up class are related to the issue. Since I have specified a start up class called ServiceKernel, I am performing the reference as such: ServiceKernel.system.actorFor. Will provide an answer if confirmed.
Confirmed that it was related to the startup class handling the Microkernel.
The ServiceKernel mentioned above is used in the start script to boot up the Microkernel JAR: ./start com.package.ServiceKernel. In an sbt shell, this isn't needed so the alternative class I provided works well for referencing an Actor System.
However, in a Microkernel the ServiceKernel appears to be using a different Actor System altogether, so if you reference that system (like I did) then actorFor lookups will always fail. I solved the problem by passing the system down into the boot classes into the specific class where I was making the actorFor reference and it worked. Did it like this (pseudo-code):
class ServiceKernel extends Bootable {
val system = ActorSystem("service-kernel")
def startup = {
system.actorOf(Props(new Boot(isDev, system))) ! Start
}
}
And then passing it to an HttpApi class:
class Boot(val isDev: Boolean, system: ActorSystem) extends Actor with SprayCanHttpServerApp {
def receive = {
case Start =>
// setup HTTP server
val service = system.actorOf(Props(new HttpApi(system)), "tracker-http-api")
}
}
I have a Play 2.1 application which does not start when I have the wrong database url. The problem is, the error message isn't that great.
[error] c.j.b.h.AbstractConnectionHook - Failed to obtain initial connection Sleeping for 0ms and trying again. A ttempts left: 0. Exception: null
Oops, cannot start the server.
Configuration error: Configuration error[Cannot connect to database [default]]
at play.api.Configuration$.play$api$Configuration$$configError(Configuration.scala:74)
at play.api.Configuration.reportError(Configuration.scala:552)
at play.api.db.BoneCPPlugin$$anonfun$onStart$1.apply(DB.scala:248)
at play.api.db.BoneCPPlugin$$anonfun$onStart$1.apply(DB.scala:239)
....
I'd like the server to dump the database url it's trying to use in this case. Does Play 2.1 provide any hooks to execute code when there is an exception during startup?
In Play Framework 2, you can override certain phases of the lifecycle by extending GlobalSettings. Specifically, onLoadConfig get called before the config is parsed and the DB connection is established.
Here's a (hacky) example of intercepting errors. You can create a fake instance of Application, then pass it the Configuration object. Then you can use it to create an instance of BoneCPPlugin and try creating a connection. In case when the DB is not reachable, you'll be able to intercept that in the catch block.
import java.io.File
import play.api._
import play.api.db.BoneCPPlugin
import scala.util.control.NonFatal
object Global extends GlobalSettings {
override def onLoadConfig(config: Configuration, path: File, classloader: ClassLoader, mode: Mode.Mode) = {
val app = new DefaultApplication(path, classloader, None, mode){
override lazy val configuration = config
}
try {
new BoneCPPlugin(app).onStart()
} catch {
case e: PlayException =>
// handle
case _ => // other types of errors that we don't care about here
}
super.onLoadConfig(config, path, classloader, mode)
}
}