I am getting a parsing exception inside of my application.conf file.
Here is my application.conf
db.test.driver = org.postgresql.Driver
db.test.user = "postgres"
db.test.password = "postgres"
db.test.url = "jdbc:postgresql://localhost:5432/gasguru"
here is the code that I am trying to use to read from my application.conf
trait DbConfigWeb extends DbConfig { qualifier: DbQualifier =>
def url: String = current.configuration.getString(qualifier + ".url").get
println(url)
def username: String = current.configuration.getString(qualifier + ".user").get
def password: String = current.configuration.getString(qualifier + ".password").get
def driver: String = current.configuration.getString(qualifier + ".driver").get
override def database: DatabaseDef = JdbcBackend.Database.forURL(url, username, password, null, driver)
override implicit val session = database createSession
}
trait DbQualifier {
val qualifier: String
}
trait DbProductionQualifier extends DbQualifier {
override val qualifier = "db.production"
}
trait DbTestQualifier extends DbQualifier {
override val qualifier = "db.test"
}
here is the test case I am trying to run:
class DbConfigWebUnitTest extends PlaySpec with OneAppPerSuite with BeforeAndAfterAll {
"DbConfigWebTest" must {
"have the same username as what is defined in application.conf" in {
val dbManagementWeb = new DbConfigWeb with DbTestQualifier
dbManagementWeb.username must be("postgres")
}
"have the same password as what is defined in application.conf" in {
val dbManagementWeb = new DbConfigWeb with DbTestQualifier
dbManagementWeb.username must be("postgres")
}
"have the qualifier db.test" in {
val dbManagementWeb = new DbConfigWeb with DbTestQualifier
dbManagementWeb.qualifier must be ("db.test")
}
}
}
and finally the error message:
[info] - must have the qualifier db.test *** FAILED ***
[info] com.typesafe.config.ConfigException$BadPath: path parameter: Invalid path 'com.suredbits.web.db.DbConfigWebUnitTest$$anonfun$1$$anonfun$apply$mcV$sp$3$$anon$3#1e2cbe08.url': Token not allowed in path expression: '$' ('$' not followed by {, '$' not allowed after '$') (you can double-quote this token if you really want it here)
[info] at com.typesafe.config.impl.Parser.parsePathExpression(Parser.java:1095)
[info] at com.typesafe.config.impl.Parser.parsePath(Parser.java:1135)
[info] at com.typesafe.config.impl.Path.newPath(Path.java:224)
[info] at com.typesafe.config.impl.SimpleConfig.hasPath(SimpleConfig.java:80)
[info] at play.api.Configuration.reportError(Configuration.scala:743)
[info] at play.api.Configuration.readValue(Configuration.scala:132)
[info] at play.api.Configuration.getString(Configuration.scala:151)
[info] at com.suredbits.web.db.DbConfigWeb$class.url(DbConfigWebProduction.scala:14)
[info] at com.suredbits.web.db.DbConfigWebUnitTest$$anonfun$1$$anonfun$apply$mcV$sp$3$$anon$3.url(DbConfigWebUnitTest.scala:27)
[info] at com.suredbits.web.db.DbConfigWeb$class.$init$(DbConfigWebProduction.scala:15)
[info] ...
You are concatenating a object with a string, which will call the default toString method that produces your.class.name#hash. You need to call the method from the trait instead:
current.configuration.getString(qualifier.qualifier + ".url").get
and do the same on the other calls.
Or you can just override the toString method to return the qualifier value and use it the same way you are using it now
trait DbQualifier {
val qualifier: String
override def toString = qualifier
}
Related
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.
So, for 3 days now I have had various problems with Play, I am new to the framework, but I don't get what is happening. So, after I was unable to use Slick, due to some futures never returning a value, I decided to switch to Anorm. It worked, until I decided to add a second repository... after which, I am now unable to load my page because I keep getting this:
#769g71c3d - Internal server error, for (GET) [/] ->
play.api.UnexpectedException: Unexpected exception[ProvisionException: Unable to provision, see the following errors:
1) Error injecting constructor, java.lang.IllegalArgumentException: Could not find database for pmf
at dao.ActivityTypeRepository.<init>(ActivityTypeDAO.scala:13)
at dao.ActivityTypeRepository.class(ActivityTypeDAO.scala:13)
while locating dao.ActivityTypeRepository
for the 1st parameter of services.ActivityTypeServiceImpl.<init>(ActivityTypeService.scala:17)
while locating services.ActivityTypeServiceImpl
while locating services.ActivityTypeService
The database is input correctly, I can connect to it via terminal and via datagrip... Has anyone ever had a similar issue?
As requested, here is my configuration:
slick.dbs.default.profile = "slick.jdbc.PostgresProfile$"
slick.dbs.default.db.driver = "org.postgresql.Driver"
slick.dbs.default.db.url = "jdbc:postgresql://localhost:5432/pmf"
These are my classes:
#Singleton
class ActivityTypeRepository #Inject()(dbapi: DBApi)(implicit ec: ExecutionContext) {
private val db = dbapi.database(RepositorySettings.dbName)
private[dao] val activityTypeMapping = {
get[Int]("activityType.id") ~
get[String]("activityType.name") map {
case id ~ name => ActivityType(id, name)
}
}
def listAll: Future[Seq[ActivityType]] = Future {
db.withConnection { implicit conn =>
SQL("SELECT * FROM ActivityType").as(activityTypeMapping *)
}
}
}
#Singleton
class UserRepository #Inject()(dbApi: DBApi)(implicit ec: ExecutionContext){
private val db = dbApi.database(RepositorySettings.dbName)
private[dao] val userMapping = {
get[Option[Long]]("users.id") ~
get[String]("users.email") ~
get[Option[String]]("users.name") ~
get[Option[String]]("users.surname") map {
case id ~ email ~ name ~ surname => User(id, email, name, surname)
}
}
def add(user: User): Future[Option[Long]] = Future {
db.withConnection { implicit conn =>
SQL"INSERT INTO users(id, email, name, surname) VALUES (${user.id}, ${user.email}, ${user.name}, ${user.surname})".executeInsert()
}
}
def find(id: Long): Future[Option[User]] = Future {
db.withConnection { implicit conn =>
SQL"SELECT * FROM User WHERE id = $id".as(userMapping *).headOption
}
}
def findByEmail(email: String): Future[Option[User]] = Future {
db.withConnection { implicit conn =>
SQL"SELECT * FROM User WHERE email = $email".as(userMapping *).headOption
}
}
def listAll: Future[Seq[User]] = Future {
db.withConnection { implicit conn =>
SQL("SELECT * FROM User").as(userMapping *)
}
}
}
EDIT:
Added to application.conf
db {
default.driver = org.postgresql.Driver
default.url = "jdbc:postgresql://localhost:5432/pmf_visualizations"
}
but no change.
I could get this working using this:
scala> object LOGGER {
| def warning(msg: String)(implicit className:String) = {
| className
| }
| }
defined object LOGGER
scala> class testing {
| lazy implicit val className = this.getClass.getName
| def test = LOGGER.warning("Testing")
| }
defined class testing
scala> val obj = new testing()
obj: testing = testing#11fb4f69
scala> obj.test
res51: String = testing <=======
scala> class testing2 {
| lazy implicit val className = this.getClass.getName
| def test = LOGGER.warning("Testing")
| }
defined class testing2
scala> val obj2 = new testing2()
obj2: testing2 = testing2#2ca3a203
scala> obj2.test
res53: String = testing2 <=====
I also tried using Thread.currentThread.getStackTrace in the object LOGGER but couldn't get it to print the calling class testing in the warning function.
Any other ways to do this?
Dynamic variable
One way to do it is DymamicVariable
import scala.util.DynamicVariable
object LOGGER {
val caller = new DynamicVariable[String]("---")
def time = new Date().toString
def warning(msg: String) = println(s"[${caller.value} : $time] $msg")
}
trait Logging {
def logged[T](action: => T) = LOGGER.caller.withValue(this.getClass.getName)(action)
}
class testing extends Logging {
def test = logged {
//some actions
LOGGER.warning("test something")
//some other actions
}
}
val t = new testing
t.test
will print something like
[testing : Wed Nov 25 11:29:23 MSK 2015] test something
Or instead of mixing in Logging you can use it directly
class testing {
def test = LOGGER.caller.withValue(this.getClass.getName) {
//some actions
LOGGER.warning("test something")
//some other actions
}
}
Macro
Another more powerfull, but more complex to support approach is to build some simple macro
You could define in other source, preferrably in other subproject
import scala.reflect.macros.blackbox.Context
import scala.language.experimental.macros
class LoggerImpl(val c: Context) {
import c.universe._
def getClassSymbol(s: Symbol): Symbol = if (s.isClass) s else getClassSymbol(s.owner)
def logImpl(msg: Expr[String]): Expr[Unit] = {
val cl = getClassSymbol(c.internal.enclosingOwner).toString
val time = c.Expr[String](q"new java.util.Date().toString")
val logline = c.Expr[String](q""" "[" + $cl + " : " + $time + "]" + $msg """)
c.Expr[Unit](q"println($logline)")
}
}
object Logger {
def warning(msg: String): Unit = macro LoggerImpl.logImpl
}
Now you don't need to change the testing class:
class testing {
def test = {
//some actions
Logger.warning("something happen")
//some other actions
}
}
And see desired output.
Thsi could be very-perfomant alternative to runtime stack introspection
I use this technique in my custom classloader project to get the name of the first class up the stack not in my package. The general idea is copied from the UrlClassloader.
String callerClassName="";
StackTraceElement[] stackTrace=Thread.currentThread().getStackTrace();
for (int i=1; i < stackTrace.length; i++) {
String candidateClassName=stackTrace[i].getClassName();
if(!candidateClassName.startsWith("to.be.ignored") &&
!candidateClassName.startsWith("java")){
callerClassName=candidateClassName;
break;
}
}
The approach has it's drawbacks since it only gets the name of the class, not the actual class or even better the object.
I am trying to read in data sources from my application.conf file, but every time I run my server, or try and run test cases, I am getting an error saying that there is no application started.
Here is an example of what I am trying to do:
Unit test that is trying to read a property from my application.conf
class DbConfigWebUnitTest extends PlaySpec with OneAppPerSuite {
implicit override lazy val app: FakeApplication = FakeApplication(
additionalConfiguration = Map("db.test.url" -> "jdbc:postgresql://localhost:5432/suredbitswebtest",
"db.test.user" -> "postgres", "db.test.password" -> "postgres", "db.test.driver" -> "org.postgresql.Driver"))
val dbManagementWeb = new DbManagementWeb with DbConfigWeb with DbTestQualifier
"DbConfigWebTest" must {
"have the same username as what is defined in application.conf" in {
dbManagementWeb.username must be("postgres")
}
}
}
Here is my DbConfigWeb
import play.api.Play.current
trait DbConfigWeb extends DbConfig { qualifier: DbQualifier =>
val url: String = current.configuration.getString(qualifier + ".url").get
val username: String = current.configuration.getString(qualifier + ".user").get
val password: String = current.configuration.getString(qualifier + ".password").get
val driver: String = current.configuration.getString(qualifier + ".driver").get
override def database: DatabaseDef = JdbcBackend.Database.forURL(url, username, password, null, driver)
override implicit val session = database createSession
}
trait DbQualifier {
val qualifier: String
}
trait DbProductionQualifier extends DbQualifier {
override val qualifier = "db.production"
}
trait DbTestQualifier extends DbQualifier {
override val qualifier = "db.test"
}
and lastly here is my stack trace:
[suredbits-web] $ last test:test
[debug] Forking tests - parallelism = false
[debug] Create a single-thread test executor
[debug] Runner for sbt.FrameworkWrapper produced 0 initial tasks for 0 tests.
[debug] Runner for org.scalatest.tools.Framework produced 2 initial tasks for 2 tests.
[debug] Running TaskDef(com.suredbits.web.db.DbConfigWebUnitTest, sbt.ForkMain$SubclassFingerscan#48687c55, false, [SuiteSelector])
[error] Uncaught exception when running com.suredbits.web.db.DbConfigWebUnitTest: java.lang.RuntimeException: There is no started application
sbt.ForkMain$ForkError: There is no started application
at scala.sys.package$.error(package.scala:27)
at play.api.Play$$anonfun$current$1.apply(Play.scala:71)
at play.api.Play$$anonfun$current$1.apply(Play.scala:71)
at scala.Option.getOrElse(Option.scala:120)
at play.api.Play$.current(Play.scala:71)
at com.suredbits.web.db.DbConfigWeb$class.$init$(DbConfigWebProduction.scala:14)
at com.suredbits.web.db.DbConfigWebUnitTest$$anon$1.<init>(DbConfigWebUnitTest.scala:14)
at com.suredbits.web.db.DbConfigWebUnitTest.<init>(DbConfigWebUnitTest.scala:14)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
at java.lang.Class.newInstance(Class.java:379)
at org.scalatest.tools.Framework$ScalaTestTask.execute(Framework.scala:641)
at sbt.ForkMain$Run$2.call(ForkMain.java:294)
at sbt.ForkMain$Run$2.call(ForkMain.java:284)
at java.util.concurrent.FutureTask.run(FutureTask.java:262)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
I think the key problem is that vals in Scala traits are initialized at construction time, which is prior to the test Play application being started (presumably its lifecycle is tied to each spec example.) You have a couple of workarounds:
make everything in DbConfigWeb a def or perhaps a lazy val
give DbConfigWeb an abstract play.api.Application field from which to extract the config values (rather than using current) and pass it explicitly (the fake application) to whatever DbManagementWeb is as a constructor parameter
Here's a simplified version, using the first approach (which works for me):
import play.api.Play.current
trait DbConfig
trait DbConfigWeb extends DbConfig {
self: DbQualifier =>
// Using defs instead of vals
def url: String = current.configuration.getString(qualifier + ".url").get
def username: String = current.configuration.getString(qualifier + ".user").get
def password: String = current.configuration.getString(qualifier + ".password").get
def driver: String = current.configuration.getString(qualifier + ".driver").get
}
trait DbQualifier {
val qualifier: String
}
trait DbTestQualifier extends DbQualifier {
override val qualifier = "db.test"
}
and the spec:
import controllers.{DbConfigWeb, DbTestQualifier}
import org.scalatestplus.play.{OneAppPerSuite, PlaySpec}
import play.api.test.FakeApplication
class DbConfigTest extends PlaySpec with OneAppPerSuite {
implicit override lazy val app: FakeApplication = FakeApplication(
additionalConfiguration = Map("db.test.url" -> "jdbc:h2:mem:play",
"db.test.user" -> "sa", "db.test.password" -> "", "db.test.driver" -> "org.h2.Driver"))
val dbManagementWeb = new DbConfigWeb with DbTestQualifier
"DbConfigWebTest" must {
"have the same username as what is defined in application.conf" in {
dbManagementWeb.username must be("sa")
}
}
}
Personally I prefer the second approach, which keeps the application state passed around explicitly rather than relying on play.api.Play.current, which you cannot rely on always being started.
You mentioned in the comments that lazy vals were not working for you but I can only conjecture that some chain of calls was forcing initialization: check again that this isn't the case.
Note also that order of initialization for vals can be complex and, while some might disagree, it's a pretty safe bet to stick to defs as trait members unless you're sure it's some expensive operation (in which case a lazy val might be an option.)
I want to retrieve a row from my default database postgres. I have table "Users" defined already.
conf/application.conf
db.default.driver=org.postgresql.Driver
db.default.url="jdbc:postgresql://localhost:5234/postgres"
db.default.user="postgres"
db.default.password=""
controllers/Application.scala
package controllers
import models.{UsersDatabase, Users}
import play.api.mvc._
object Application extends Controller {
def index = Action {
Ok(views.html.index(UsersDatabase.getAll))
}
}
models/Users.scala
package models
import java.sql.Date
import play.api.Play.current
import play.api.db.DB
import slick.driver.PostgresDriver.simple._
case class User(
id: Int,
username: String,
password: String,
full_name: String,
email: String,
gender: String,
dob: Date,
joined_date: Date
)
class Users(tag: Tag) extends Table[User](tag, "Users") {
def id = column[Int]("id")
def username = column[String]("username", O.PrimaryKey)
def password = column[String]("password")
def full_name = column[String]("full_name")
def email = column[String]("email")
def gender = column[String]("gender")
def dob = column[Date]("dob")
def joined_date = column[Date]("joined_date")
def * = (id, username, password, full_name, email, gender, dob, joined_date) <> (User.tupled, User.unapply)
}
object UsersDatabase {
def getAll: List[User] = {
Database.forDataSource(DB.getDataSource()) withSession {
Query(Users).list
}
}
}
While accessing http://localhost:9000/ it gives compilation error:
[error] .../app/models/Users.scala:36: not found: value Users
[error] Query(Users).list
[error] ^
[error] one error found
[error] (compile:compile) Compilation failed
How to resolve this error and access data properly?
The compilation error message says it all - there's no value Users to use in the scope.
Change the object UsersDatabase to look as follows:
object UsersDatabase {
val users = TableQuery[Users]
def getAll: List[User] = {
Database.forDataSource(DB.getDataSource()) withSession { implicit session =>
users.list
}
}
}
And the error goes away since you're using the local val users to list users in the database.
As described in Querying in the official documentation of Slick session val is an implicit value of list (as final def list(implicit session: SessionDef): List[R]), and hence implicit session in the block:
All methods that execute a query take an implicit Session value. Of
course, you can also pass a session explicitly if you prefer:
val l = q.list(session)