Play Framework 2.6, Postgresql. Jooq as db access library.
When running test, I'm getting
org.postgresql.util.PSQLException: FATAL: sorry, too many clients
already
Here is a helper class which provides jooq's dsl context:
#Singleton
class Db #Inject() (val db: Database, system: ActorSystem) {
val databaseContext: ExecutionContext = system.dispatchers.lookup("contexts.database")
def query[A](block: DSLContext => A): Future[A] = Future {
db.withConnection { connection: Connection =>
val dsl = DSL.using(connection, SQLDialect.POSTGRES_9_4)
block(dsl)
}
}(databaseContext)
def withTransaction[A](block: DSLContext => A): Future[A] = Future {
db.withTransaction { connection: Connection =>
val dsl: DSLContext = DSL.using(connection, SQLDialect.POSTGRES_9_4)
block(dsl)
}
}(databaseContext)
}
I use this helper class in repositories like this:
db.query { dsl =>
val records = dsl
.selectFrom(USERS)
.where(...)
...
}
}
application.conf
db.default.driver="org.postgresql.Driver"
db.default.url="jdbc:postgresql://localhost/postgres?user=postgres"
...
contexts {
database {
executor = "thread-pool-executor"
throughput = 1
thread-pool-executor {
fixed-pool-size = 9
}
}
}
...
build.sbt
...
libraryDependencies += jdbc
libraryDependencies += "org.jooq" % "jooq" % "3.10.5"
libraryDependencies += "org.jooq" % "jooq-codegen-maven" % "3.10.5"
libraryDependencies += "org.jooq" % "jooq-meta" % "3.10.5"
libraryDependencies += "org.postgresql" % "postgresql" % "42.1.4"
...
And base trait for all my tests for any case:
class BaseFeatureSpec extends FeatureSpec
with GivenWhenThen
with GuiceOneServerPerSuite
with Matchers
with WsScalaTestClient
with BeforeAndAfterEach
with MockitoSugar {
override def fakeApplication(): Application =
new GuiceApplicationBuilder()
.overrides(bind[EmailService].to(classOf[EmailServiceStub]))
.build()
def config: Configuration = fakeApplication().configuration
def actorSystem: ActorSystem = fakeApplication().actorSystem
val db: Db = app.injector.instanceOf[Db]
val wsClient: WSClient = app.injector.instanceOf[WSClient]
val myPublicAddress = s"localhost:$port"
private val injector = fakeApplication().injector
def truncateDbOnEachRun = true
override protected def beforeEach(): Unit = {
if (truncateDbOnEachRun) {
truncateDb
}
}
protected def truncateDb = {
await(db.withTransaction { dsl =>
... truncate all dbs...
})
}
}
max_connections of my postgresql instance is 100.
What I noticed is that when running test, I see that pool is created multiple times almost before each test:
[info] application - Creating Pool for datasource 'default'
[info] application - Creating Pool for datasource 'default'
[info] application - Creating Pool for datasource 'default'
[info] application - Creating Pool for datasource 'default'
And after I'm getting too many connections error.
Please help.
Look like you use a dispatcher with default type, try to add type = PinnedDispatcher, which is used for io type dispatcher, as follow.
thread-pool-executor {
...
type = PinnedDispatcher
}
And you can find details from
https://doc.akka.io/docs/akka/2.5/dispatchers.html
I'v found the issue:
override def fakeApplication(): Application =
new GuiceApplicationBuilder()
.overrides(bind[EmailService].to(classOf[EmailServiceStub]))
.build()
Creates new application instance (because it is method) each time I access to it like app.injector.instanceOf[Db], so that's why I have so many application - Creating Pool for datasource 'default' logs.
I've replaced def with val
override val fakeApplication(): Application =
new GuiceApplicationBuilder()
.overrides(bind[EmailService].to(classOf[EmailServiceStub]))
.build()
And it works fine.
Related
I try to develop gRPC server with Akka-gRPC and Slick. I also use Airframe for DI.
Source code is here
The issue is that it cause failure if it receive request when execute as gRPC server.
If it doesn't start as a gRPC server, but just reads resources from the database, the process succeeds.
What is the difference?
At Follows, It read object from database with slick.
...Component is airframe object. It will use by main module.
trait UserRepository {
def getUser: Future[Seq[Tables.UsersRow]]
}
class UserRepositoryImpl(val profile: JdbcProfile, val db: JdbcProfile#Backend#Database) extends UserRepository {
import profile.api._
def getUser: Future[Seq[Tables.UsersRow]] = db.run(Tables.Users.result)
}
trait UserResolveService {
private val repository = bind[UserRepository]
def getAll: Future[Seq[Tables.UsersRow]] =
repository.getUser
}
object userServiceComponent {
val design = newDesign
.bind[UserResolveService]
.toSingleton
}
Follows is gRPC Server source code.
trait UserServiceImpl extends UserService {
private val userResolveService = bind[UserResolveService]
private val system: ActorSystem = bind[ActorSystem]
implicit val ec: ExecutionContextExecutor = system.dispatcher
override def getAll(in: GetUserListRequest): Future[GetUserListResponse] = {
userResolveService.getAll.map(us =>
GetUserListResponse(
us.map(u =>
myapp.proto.user.User(
1,
"t_horikoshi#example.com",
"t_horikoshi",
myapp.proto.user.User.UserRole.Admin
)
)
)
)
}
}
trait GRPCServer {
private val userServiceImpl = bind[UserServiceImpl]
implicit val system: ActorSystem = bind[ActorSystem]
def run(): Future[Http.ServerBinding] = {
implicit def ec: ExecutionContext = system.dispatcher
val service: PartialFunction[HttpRequest, Future[HttpResponse]] =
UserServiceHandler.partial(userServiceImpl)
val reflection: PartialFunction[HttpRequest, Future[HttpResponse]] =
ServerReflection.partial(List(UserService))
// Akka HTTP 10.1 requires adapters to accept the new actors APIs
val bound = Http().bindAndHandleAsync(
ServiceHandler.concatOrNotFound(service, reflection),
interface = "127.0.0.1",
port = 8080,
settings = ServerSettings(system)
)
bound.onComplete {
case Success(binding) =>
system.log.info(
s"gRPC Server online at http://${binding.localAddress.getHostName}:${binding.localAddress.getPort}/"
)
case Failure(ex) =>
system.log.error(ex, "occurred error")
}
bound
}
}
object grpcComponent {
val design = newDesign
.bind[UserServiceImpl]
.toSingleton
.bind[GRPCServer]
.toSingleton
}
Follows is main module.
object Main extends App {
val conf = ConfigFactory
.parseString("akka.http.server.preview.enable-http2 = on")
.withFallback(ConfigFactory.defaultApplication())
val system = ActorSystem("GRPCServer", conf)
val dbConfig: DatabaseConfig[JdbcProfile] =
DatabaseConfig.forConfig[JdbcProfile](path = "mydb")
val design = newDesign
.bind[JdbcProfile]
.toInstance(dbConfig.profile)
.bind[JdbcProfile#Backend#Database]
.toInstance(dbConfig.db)
.bind[UserRepository]
.to[UserRepositoryImpl]
.bind[ActorSystem]
.toInstance(system)
.add(userServiceComponent.design)
.add(grpcComponent.design)
design.withSession(s =>
// Await.result(s.build[UserResolveService].getUser, Duration.Inf)) // success
// Await.result(s.build[UserServiceImpl].getAll(GetUserListRequest()), Duration.Inf)) // success
s.build[GRPCServer].run() // cause IllegalStateException when reciece request.
)
}
When UserResolveService and UserServiceImpl are called directly, the process of loading an object from the database is successful.
However, when running the application as a gRPC Server, an error occurs when a request is received.
Though I was thinking all day, I couldn't resolve...
Will you please help me to resolve.
It resolved. if execute async process, It has to start gRPC server with newSession.
I fix like that.
I am trying to get connection from default Hikaricp config. Following is application.conf
modules {
enabled += "play.api.db.DBModule"
enabled += "play.api.db.HikariCPModule"
enabled += "modules.AppModule"
}
db.default.hikaricp.dataSourceClassName=org.postgresql.ds.PGSimpleDataSource
db.default.hikaricp.dataSource.user=rp
#db.default.hikaricp.dataSource.url="postgres://rp:password#localhost/profile"
db.default.hikaricp.dataSource.password=password
db.default.hikaricp.dataSource.databaseName=profile
db.default.hikaricp.dataSource.serverName=localhost
db.default.hikaricp.connectionTestQuery = "SELECT 1"
Since Play is maintaining the connection pool. Now I am unable to find a way to get connection created above Play. I have tried the following (1, 2, 3):
//1. Injecting
import slick.jdbc.PostgresProfile.api._
class DBConnection #Inject()(db: Database) {
}
// 2. Mentioned here.
object DBConnection {
implicit val db = DatabaseConfigProvider.get[JdbcProfile](Play.current).db
//implicit val db = Database.forConfig("default") (3)
}
How can I get connection from the default connection pool?
Additional Details:
Play Framework version: 2.16.9
Scala version is 2.12.6
Postgres Dependency org.postgresql" % "postgresql" % "9.4-1206-jdbc42.
Added logs here for all three try.
You can use play.api.db.DBApi, like:
class DatabaseService #Inject()(dbApi: DBApi)
(implicit ec: DatabaseExecutionContext) {
lazy val database = dbApi.database("default")
...
}
So, I have this dependency which is used to create tables and interact with Postgres. Here is a Sample Class:
class ConfigTable {
this: DBFactory =>
import driver.api._
implicit val configKeyMapper = MappedColumnType.base[ConfigKey, String](e => e.toString, s => ConfigKey.withName(s))
val configs = TableQuery[ConfigMapping]
class ConfigMapping(tag: Tag) extends Table[Config](tag, "configs") {
def key = column[ConfigKey]("key")
def value = column[String]("value")
def * = (key, value) <> (Config.tupled, Config.unapply _)
}
/**
* add config
*
* #param config
* #return
*/
def add(config: Config): Try[Config] = try {
sync(db.run(configs += config)) match {
case 1 => Success(config)
case _ => Failure(new Exception("Unable to add config"))
}
} catch {
case ex: PSQLException =>
if (ex.getMessage.contains("duplicate key value")) Failure(new Exception("alt id already exists."))
else Failure(new Exception(ex.getMessage))
}
def get(key: ConfigKey): Option[Config] = sync(db.run(configs.filter(x => x.key === key).result)).headOption
def getAll(): Seq[Config] = sync(db.run(configs.result))
}
object ConfigTable extends ConfigTable with PSQLComponent
PSQLComponent is the Abstraction for Database meta configuration:
import slick.jdbc.PostgresProfile
trait PSQLComponent extends DBFactory {
val driver = PostgresProfile
import driver.api.Database
val db: Database = Database.forConfig("db.default")
}
DBFactory is again an abstraction:
import slick.jdbc.JdbcProfile
trait DBFactory {
val driver: JdbcProfile
import driver.api._
val db: Database
}
application.conf:
db.default {
driver = "org.postgresql.Driver"
url = "jdbc:postgresql://localhost:5432/db"
user = "user"
password = "pass"
hikaricp {
minimumIdle = ${db.default.async-executor.minConnections}
maximumPoolSize = ${db.default.async-executor.maxConnections}
}
}
jdbc-defaults.slick.profile = "slick.jdbc.PostgresProfile$"
lagom.persistence.jdbc.create-tables.auto=false
I compile and publish this dependency to nexus and trying to use this in my Lagom Microservice.
Here is the Loader Class:
class SlickExapleAppLoader extends LagomApplicationLoader {
override def load(context: LagomApplicationContext): LagomApplication = new SlickExampleApp(context) {
override def serviceLocator: ServiceLocator = NoServiceLocator
}
override def loadDevMode(context: LagomApplicationContext): LagomApplication = new SlickExampleApp(context) with LagomDevModeComponents {
}
override def describeService = Some(readDescriptor[SlickExampleLMSServiceImpl])
}
abstract class SlickExampleApp(context: LagomApplicationContext)
extends LagomApplication(context)
// No Idea which to use and how, nothing clear from doc too.
// with ReadSideJdbcPersistenceComponents
// with ReadSideSlickPersistenceComponents
// with SlickPersistenceComponents
with AhcWSComponents {
wire[SlickExampleScheduler]
}
I'm trying to implement it in this scheduler:
class SlickExampleScheduler #Inject()(lmsService: LMSService,
configuration: Configuration)(implicit ec: ExecutionContext) {
val brofile = `SomeDomainObject`
val gson = new Gson()
val concurrency = Runtime.getRuntime.availableProcessors() * 10
implicit val timeout: Timeout = 3.minute
implicit val system: ActorSystem = ActorSystem("LMSActorSystem")
implicit val materializer: ActorMaterializer = ActorMaterializer()
// Getting Exception Initializer here..... For ConfigTable ===> ExceptionLine
val schedulerImplDao = new SchedulerImplDao(ConfigTable)
def hitLMSAPI = {
println("=============>1")
schedulerImplDao.doSomething()
}
system.scheduler.schedule(2.seconds, 2.seconds) {
println("=============>")
hitLMSAPI
}
}
Not sure if it's the correct way, or if it's not what is the correct way of doing this. It is the project requirement to keep the Data Models separate from the service for the obvious reasons of re-usability.
Exception Stack:
17:50:38.666 [info] akka.cluster.Cluster(akka://lms-impl-application) [sourceThread=ForkJoinPool-1-worker-1, akkaTimestamp=12:20:38.665UTC, akkaSource=akka.cluster.Cluster(akka://lms-impl-application), sourceActorSystem=lms-impl-application] - Cluster Node [akka.tcp://lms-impl-application#127.0.0.1:45805] - Started up successfully
17:50:38.707 [info] akka.cluster.Cluster(akka://lms-impl-application) [sourceThread=lms-impl-application-akka.actor.default-dispatcher-6, akkaTimestamp=12:20:38.707UTC, akkaSource=akka.cluster.Cluster(akka://lms-impl-application), sourceActorSystem=lms-impl-application] - Cluster Node [akka.tcp://lms-impl-application#127.0.0.1:45805] - No seed-nodes configured, manual cluster join required
java.lang.ExceptionInInitializerError
at com.slick.init.impl.SlickExampleScheduler.<init>(SlickExampleScheduler.scala:29)
at com.slick.init.impl.SlickExampleApp.<init>(SlickExapleAppLoader.scala:42)
at com.slick.init.impl.SlickExapleAppLoader$$anon$2.<init>(SlickExapleAppLoader.scala:17)
at com.slick.init.impl.SlickExapleAppLoader.loadDevMode(SlickExapleAppLoader.scala:17)
at com.lightbend.lagom.scaladsl.server.LagomApplicationLoader.load(LagomApplicationLoader.scala:76)
at play.core.server.LagomReloadableDevServerStart$$anon$1.$anonfun$get$5(LagomReloadableDevServerStart.scala:176)
at play.utils.Threads$.withContextClassLoader(Threads.scala:21)
at play.core.server.LagomReloadableDevServerStart$$anon$1.$anonfun$get$3(LagomReloadableDevServerStart.scala:173)
at scala.Option.map(Option.scala:163)
at play.core.server.LagomReloadableDevServerStart$$anon$1.$anonfun$get$2(LagomReloadableDevServerStart.scala:149)
at scala.util.Success.flatMap(Try.scala:251)
at play.core.server.LagomReloadableDevServerStart$$anon$1.$anonfun$get$1(LagomReloadableDevServerStart.scala:147)
at scala.concurrent.Future$.$anonfun$apply$1(Future.scala:658)
at scala.util.Success.$anonfun$map$1(Try.scala:255)
at scala.util.Success.map(Try.scala:213)
at scala.concurrent.Future.$anonfun$map$1(Future.scala:292)
at scala.concurrent.impl.Promise.liftedTree1$1(Promise.scala:33)
at scala.concurrent.impl.Promise.$anonfun$transform$1(Promise.scala:33)
at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:64)
at java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1402)
at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
Caused by: java.lang.NullPointerException
at com.example.db.models.LoginTable.<init>(LoginTable.scala:29)
at com.example.db.models.LoginTable$.<init>(LoginTable.scala:293)
at com.example.db.models.LoginTable$.<clinit>(LoginTable.scala)
... 24 more
This is how it is woking:
abstract class SlickExampleApp(context: LagomApplicationContext) extends LagomApplication(context)
with SlickPersistenceComponents with AhcWSComponents {
override implicit lazy val actorSystem: ActorSystem = ActorSystem("LMSActorSystem")
override lazy val materializer: ActorMaterializer = ActorMaterializer()
override lazy val lagomServer = serverFor[SlickExampleLMSService](wire[SlickExampleLMSServiceImpl])
lazy val externalService = serviceClient.implement[LMSService]
override def connectionPool: ConnectionPool = new HikariCPConnectionPool(environment)
override def jsonSerializerRegistry: JsonSerializerRegistry = new JsonSerializerRegistry {
override def serializers: immutable.Seq[JsonSerializer[_]] = Vector.empty
}
val loginTable = wire[LoginTable]
wire[SlickExampleScheduler]
}
> One thing I'd like to report is: Lagom docs about the application.conf configuration of slick is not correct, it misleaded me for two days, the I digged into the Liberary code and this is how it goes:
private val readSideConfig = system.settings.config.getConfig("lagom.persistence.read-side.jdbc")
private val jdbcConfig = system.settings.config.getConfig("lagom.persistence.jdbc")
private val createTables = jdbcConfig.getConfig("create-tables")
val autoCreateTables: Boolean = createTables.getBoolean("auto")
// users can disable the usage of jndiDbName for userland read-side operations by
// setting the jndiDbName to null. In which case we fallback to slick.db.
// slick.db must be defined otherwise the application will fail to start
val db = {
if (readSideConfig.hasPath("slick.jndiDbName")) {
new InitialContext()
.lookup(readSideConfig.getString("slick.jndiDbName"))
.asInstanceOf[Database]
} else if (readSideConfig.hasPath("slick.db")) {
Database.forConfig("slick.db", readSideConfig)
} else {
throw new RuntimeException("Cannot start because read-side database configuration is missing. " +
"You must define either 'lagom.persistence.read-side.jdbc.slick.jndiDbName' or 'lagom.persistence.read-side.jdbc.slick.db' in your application.conf.")
}
}
val profile = DatabaseConfig.forConfig[JdbcProfile]("slick", readSideConfig).profile
The configuration it requires is very much different than the suggested one on the Doc.
I have problems with evolutions when running tests in play framework using
playframework v2.6.6 for scala
play-slick v3.0.2
play-slick-evolutions v3.0.2
The test looks like this:
class TestFooController extends PlaySpec with GuiceOneServerPerSuite {
"foo endpoint should store some data" in {
val wsClient = app.injector.instanceOf[WSClient]
val url = s"http://localhost:$port/foo"
val requestData = Json.obj("foo" -> "bar")
val response = await(wsClient.url(url).post(requestData))
response.status mustBe OK
}
}
The database configuration looks like this:
slick.dbs.default.driver="slick.driver.H2Driver$"
slick.dbs.default.db.driver="org.h2.Driver"
slick.dbs.default.db.url="jdbc:h2:mem:play"
Asume there is an evolution script which creates the table foos and this script is working fine in dev mode.
When running the test the following error is thrown:
play.api.http.HttpErrorHandlerExceptions$$anon$1: Execution exception[[JdbcSQLException: Table "foos" not found;
The table foos could not be found so I assume that database evolutions have not been applied.
Then I changed the database configuration to postgresql which is used in dev mode.
slick.dbs.default.driver = "slick.driver.PostgresDriver$"
slick.dbs.default.db.driver = "org.postgresql.Driver"
slick.dbs.default.db.url = "jdbc:postgresql://localhost:5432/foo-test"
slick.dbs.default.db.user = "user"
slick.dbs.default.db.password = "password"
With this configuration the test work fine and data are stored in the database, so database evolutions ran just fine.
Now the problem is, that the database is not cleaned up after tests. I'd like to run each test suite with a clean database.
To sum up. With H2Db evolutions are not applied, with postgresql evolutions are applied but not cleaned up.
Even if this explicitly defined in application.test.conf
play.evolutions.autoApply=true
play.evolutions.autoApplyDowns=true
I also tried
play.evolutions.db.default.autoApply=true
play.evolutions.db.default.autoApplyDowns=true
no effect.
Then I tried to do this manually via:
def withManagedDatabase[T](block: Database => T): Unit = {
val dbapi = app.injector.instanceOf[DBApi]
val database = dbapi.database("default")
Evolutions.applyEvolutions(database)
block(database)
Evolutions.cleanupEvolutions(database)
}
and then changing the test to:
"foo endpoint should store some data" in withManagedDatabase { _ =>
...
}
For the H2 database configuration it has no effect, the same error that table foos can not be found is thrown. For the postgresql database configuration an evolution exceptions is thrown
play.api.db.evolutions.InconsistentDatabase: Database 'default' is in an inconsistent state![An evolution has not been applied properly. Please check the problem and resolve it manually before marking it as resolved.]
I want evolution ups running before and evolution downs running after each test suite. How can this be achieved?
You can use the following to apply evolutions before each suite and clean up afterwards:
trait DatabaseSupport extends BeforeAndAfterAll {
this: Suite with ServerProvider =>
private lazy val db = app.injector.instanceOf[DBApi]
override protected def beforeAll(): Unit = {
super.beforeAll()
initializeEvolutions(db.database("default"))
}
override protected def afterAll(): Unit = {
cleanupEvolutions(db.database("default"))
super.afterAll()
}
private def initializeEvolutions(database: Database):Unit = {
Evolutions.cleanupEvolutions(database)
Evolutions.applyEvolutions(database)
}
private def cleanupEvolutions(database: Database):Unit = {
Evolutions.cleanupEvolutions(database)
}
}
This is working for me:
class DAOSpec extends PlaySpec with GuiceOneAppPerSuite {
val dbUrl = sys.env.getOrElse("DATABASE_URL", "postgres://foo:password#localhost:5432/foo")
val testConfig = Map("db.default.url" -> dbUrl)
implicit override def fakeApplication() = new GuiceApplicationBuilder().configure(testConfig).build()
lazy val database = app.injector.instanceOf[Database]
lazy val dao = app.injector.instanceOf[DAO]
"create" must {
"work" in Evolutions.withEvolutions(database) {
val foo = await(dao.create("foo"))
foo.id must not be null
}
}
}
I'm not meaning the way play disabling PlayNettyServer and enable AkkaHttpServer which is described in the documentation by using:
lazy val root = (project in file("."))
.enablePlugins(PlayScala, PlayAkkaHttpServer)
.disablePlugins(PlayNettyServer)
I'm meaning that taking advantage of play framework's dependency injection and other tools like play-slick, and use akka-http directly in code like:
class AppInitiation #Inject()(implicit val system: ActorSystem, configuration: Configuration) {
implicit val materializer = ActorMaterializer()
implicit val timeout: Timeout = 5 seconds
val logger = Logger("Server")
val milkywayPath = path(Segment ~ RestPath)
val methods = get | put | post | delete
val gatewayExceptionHandler = ExceptionHandler {
case e: AskTimeoutException =>
complete(HttpResponse(InternalServerError, Nil, "Ask timeout!"))
case e: Exception =>
logger.error("unknown error", e)
complete(HttpResponse(InternalServerError, Nil, "Unknown error! Please contact administratro!"))
}
implicit def rejectionHandler = RejectionHandler.newBuilder()
.handle { case MissingHeaderRejection("X-Caller-Service") =>
complete(HttpResponse(BadRequest, Nil, "Missing required header X-Caller-Service!"))
}
.handle { case MissingQueryParamRejection("v") =>
complete(HttpResponse(BadRequest, Nil, "Missing required parameter v!"))
}
.result()
val requestHandler = system.actorOf(Props(new RequestHandler))
val routes = handleExceptions(gatewayExceptionHandler) {
(milkywayPath & methods & parameter('v.as[String]) & headerValueByName("X-Caller-Service") & extractRequest) {
(contextPath: String, resource: Path, v, caller, request) =>
complete {
val startTime = System.currentTimeMillis()
val callerInfo = CallerInfo(caller, contextPath, v, resource.toString())
val f = (requestHandler ? RequestHandlerMsg(callerInfo, request)).mapTo[HttpResponse]
f onComplete {
case r => accessLogger.info(s"method=${request.method.name} " +
s"uri=${request.uri} " +
s"caller=$caller " +
s"totalTime=${System.currentTimeMillis() - startTime}ms " +
s"resultStatus=${r.map(_.status.intValue).getOrElse(500)}")
}
f
}
}
}
val host = configuration.getString("gateway.host").getOrElse("localhost")
val port = configuration.getInt("gateway.port").getOrElse(9001)
Http().bindAndHandle(routes, host, port).onComplete(_ => logger.info(s"Server started listening request on port $port"))
}
and in module I can set it as:
override def configure(): Unit = {
bind(classOf[AppInitiation]).asEagerSingleton()
}
This works. But I still want to know how I can start it without PlayNettyServer running. I've tried:
lazy val `root` = (project in file("."))
.enablePlugins(PlayScala)
.disablePlugins(PlayNettyServer)
However there emerges the exception:
[info] p.a.l.c.ActorSystemProvider - Starting application default Akka system: application
play.core.server.ServerStartException: No ServerProvider configured with key 'play.server.provider'
at play.core.server.ServerProvider$$anonfun$1.apply(ServerProvider.scala:54)
at play.core.server.ServerProvider$$anonfun$1.apply(ServerProvider.scala:54)
I'm wondering if there is a way to take full advantage of Play framework with all its features and also build a higher performance server with akka http.