How do I instantiate an object from this class? I have another class where I would like to use the MongoUtils class as defined below. This class is from reactivemongo
package controllers
import javax.inject.Inject
import scala.concurrent.Future
import play.api.Logger
import play.api.mvc.{ Action, Controller }
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import play.api.libs.functional.syntax._
import play.api.libs.json._
// Reactive Mongo imports
import reactivemongo.api.Cursor
import play.modules.reactivemongo.{ // ReactiveMongo Play2 plugin
MongoController,
ReactiveMongoApi,
ReactiveMongoComponents
}
// BSON-JSON conversions/collection
import reactivemongo.play.json._
import play.modules.reactivemongo.json.collection._
/*
* Example using ReactiveMongo + Play JSON library.
*
* There are two approaches demonstrated in this controller:
* - using JsObjects directly
* - using case classes that can be turned into JSON using Reads and Writes.
*
* This controller uses JsObjects directly.
*
* Instead of using the default Collection implementation (which interacts with
* BSON structures + BSONReader/BSONWriter), we use a specialized
* implementation that works with JsObject + Reads/Writes.
*
* Of course, you can still use the default Collection implementation
* (BSONCollection.) See ReactiveMongo examples to learn how to use it.
*/
class MongoUtils #Inject() (val reactiveMongoApi: ReactiveMongoApi)
extends Controller with MongoController with ReactiveMongoComponents {
/*
* Get a JSONCollection (a Collection implementation that is designed to work
* with JsObject, Reads and Writes.)
* Note that the `collection` is not a `val`, but a `def`. We do _not_ store
* the collection reference to avoid potential problems in development with
* Play hot-reloading.
*/
def collection: JSONCollection = db.collection[JSONCollection]("persons")
def index = Action { Ok("works") }
def create(name: String, age: Int) = Action.async {
val json = Json.obj(
"name" -> name,
"age" -> age,
"created" -> new java.util.Date().getTime())
collection.insert(json).map(lastError =>
Ok("Mongo LastError: %s".format(lastError)))
}
def createFromJson = Action.async(parse.json) { request =>
import play.api.libs.json.Reads._
/*
* request.body is a JsValue.
* There is an implicit Writes that turns this JsValue as a JsObject,
* so you can call insert() with this JsValue.
* (insert() takes a JsObject as parameter, or anything that can be
* turned into a JsObject using a Writes.)
*/
val transformer: Reads[JsObject] =
Reads.jsPickBranch[JsString](__ \ "firstName") and
Reads.jsPickBranch[JsString](__ \ "lastName") and
Reads.jsPickBranch[JsNumber](__ \ "age") reduce
request.body.transform(transformer).map { result =>
collection.insert(result).map { lastError =>
Logger.debug(s"Successfully inserted with LastError: $lastError")
Created
}
}.getOrElse(Future.successful(BadRequest("invalid json")))
}
def findByName(name: String) = Action.async {
// let's do our query
val cursor: Cursor[JsObject] = collection.
// find all people with name `name`
find(Json.obj("name" -> name)).
// sort them by creation date
sort(Json.obj("created" -> -1)).
// perform the query and get a cursor of JsObject
cursor[JsObject]()
// gather all the JsObjects in a list
val futurePersonsList: Future[List[JsObject]] = cursor.collect[List]()
// transform the list into a JsArray
val futurePersonsJsonArray: Future[JsArray] =
futurePersonsList.map { persons => Json.arr(persons) }
// everything's ok! Let's reply with the array
futurePersonsJsonArray.map { persons =>
Ok(persons)
}
}
}
So far I have tried val mongoutil = new MongoUtils(reactiveMongoApi = play.modules.reactivemongo)
but apparently it does not work.
You can use Play’s dependency injection mechanism to resolve instance of ReactiveMongoApi which is the interface to MongoDB. The steps could be:
Add the lines below to application.conf
play.modules.enabled += "play.modules.reactivemongo.ReactiveMongoModule"
play.modules.enabled += "modules.CommonModule"
modules.CommonModule is the place you define your bindings, so that Guice, a DI framework integrated with Play 2.4, knows which implementation to be used for a particular interface. For example,
package modules
import com.google.inject.AbstractModule
import dao.impl.UserDaoMongo
import dao.UserDao
import play.api.{Configuration, Environment}
import services.{UserService, UserServiceImpl}
case class CommonModule(environment: Environment, configuration: Configuration) extends AbstractModule {
def configure() {
bindDAOs()
bindServices()
}
private def bindDAOs(): Unit = {
bind(classOf[UserDao]).to(classOf[UserDaoMongo])
}
def bindServices(): Unit = {
bind(classOf[UserService]).to(classOf[UserServiceImpl])
}
}
In addition, it is a good practice to make controllers singleton. For example:
import javax.inject.{Singleton, Inject}
#Singleton
class UserController #Inject()(userService: UserService) extends Controller {
Please read this to know how to use Reactivemongo with Play framework. On the other hand, this is a complete example: https://github.com/luongbalinh/play-mongo
If you're using this class from a controller, you should annotate your controller as follows to have play inject an instance into it:
class MyController #Inject() (mongoUtils: MongoUtils) extends Controller {}
If you are not using the injected controllers, you can access it via the injector:
Play.current.injector.instanceOf(classOf[MongoUtils])
Read more about dependency injection in play here: https://www.playframework.com/documentation/2.4.x/ScalaDependencyInjection
Related
This is my code:
package controllers
import javax.inject.Inject
import scala.concurrent.Future
import play.api.Logger
import play.api.mvc.{ Action, Controller }
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import play.api.libs.functional.syntax._
import play.api.libs.json._
// Reactive Mongo imports
import reactivemongo.api.Cursor
import reactivemongo.api.ReadPreference
import play.modules.reactivemongo.{ // ReactiveMongo Play2 plugin
MongoController,
ReactiveMongoApi,
ReactiveMongoComponents
}
// BSON-JSON conversions/collection
import reactivemongo.play.json._
import play.modules.reactivemongo.json.collection._
/*
* Example using ReactiveMongo + Play JSON library.
*
* There are two approaches demonstrated in this controller:
* - using JsObjects directly
* - using case classes that can be turned into JSON using Reads and Writes.
*
* This controller uses JsObjects directly.
*
* Instead of using the default Collection implementation (which interacts with
* BSON structures + BSONReader/BSONWriter), we use a specialized
* implementation that works with JsObject + Reads/Writes.
*
* Of course, you can still use the default Collection implementation
* (BSONCollection.) See ReactiveMongo examples to learn how to use it.
*/
class Application #Inject() (val reactiveMongoApi: ReactiveMongoApi)
extends Controller with MongoController with ReactiveMongoComponents {
def collection: Future[JSONCollection] =
database.map(_.collection[JSONCollection]("Persons"))
def index = Action { Ok("works") }
def create(name: String, age: Int) = Action.async {
val json = Json.obj(
"name" -> name,
"age" -> age,
"created" -> new java.util.Date().getTime())
collection.flatMap(_.insert(json)).map(lastError =>
Ok("Mongo LastError: %s".format(lastError)))
}
}
I´ve an error that said can´t find database value (database is a member of ReactiveMongoApi Trait) so I guess there is something wrong with dependency injection. I added also this line to the build sbt:
adding routesGenerator := InjectedRoutesGenerator
Any ideas? thank you!
You need to refer to the injected API. That is, replace database with reactiveMongoApi.database:
class Application #Inject() (val reactiveMongoApi: ReactiveMongoApi)
extends Controller with MongoController with ReactiveMongoComponents {
def collection: Future[JSONCollection] =
reactiveMongoApi.database.map(_.collection[JSONCollection]("Persons"))
...
}
I'm trying to follow the tutorial of "reactiveMongo" with play framework and scala, but I get the following error:
[ConnectionNotInitialized: MongoError['Connection is missing metadata (like protocol version, etc.) The connection pool is probably being initialized.']]
My application.conf haves:
play.modules.enabled += "play.modules.reactivemongo.ReactiveMongoModule"
mongodb.uri ="mongodb://localhost:27017/cambio"
My build.sbt haves:
libraryDependencies += "org.reactivemongo" %% "play2-reactivemongo" % "0.12.5-play26"
My mongo is start.
My controller is:
package controllers
import javax.inject.Inject
import scala.concurrent.Future
import play.api.Logger
import play.api.mvc.{ Action, Controller }
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import play.api.libs.functional.syntax._
import play.api.libs.json._
// Reactive Mongo imports
import reactivemongo.api.Cursor
import reactivemongo.api.ReadPreference
import play.modules.reactivemongo.{ // ReactiveMongo Play2 plugin
MongoController,
ReactiveMongoApi,
ReactiveMongoComponents
}
// BSON-JSON conversions/collection
import reactivemongo.play.json._
import play.modules.reactivemongo.json.collection._
/*
* Example using ReactiveMongo + Play JSON library.
*
* There are two approaches demonstrated in this controller:
* - using JsObjects directly
* - using case classes that can be turned into JSON using Reads and Writes.
*
* This controller uses JsObjects directly.
*
* Instead of using the default Collection implementation (which interacts with
* BSON structures + BSONReader/BSONWriter), we use a specialized
* implementation that works with JsObject + Reads/Writes.
*
* Of course, you can still use the default Collection implementation
* (BSONCollection.) See ReactiveMongo examples to learn how to use it.
*/
class UserController #Inject() (val reactiveMongoApi: ReactiveMongoApi)
extends Controller with MongoController with ReactiveMongoComponents {
/*
* Get a JSONCollection (a Collection implementation that is designed to work
* with JsObject, Reads and Writes.)
* Note that the `collection` is not a `val`, but a `def`. We do _not_ store
* the collection reference to avoid potential problems in development with
* Play hot-reloading.
*/
def collection: JSONCollection = db.collection[JSONCollection]("broker")
def index = Action { Ok("works") }
def create(name: String, age: Int) = Action.async {
val json = Json.obj(
"name" -> name,
"age" -> age,
"created" -> new java.util.Date().getTime())
collection.insert(json).map(lastError =>
Ok("Mongo LastError: %s".format(lastError)))
}
def createFromJson = Action.async(parse.json) { request =>
import play.api.libs.json.Reads._
/*
* request.body is a JsValue.
* There is an implicit Writes that turns this JsValue as a JsObject,
* so you can call insert() with this JsValue.
* (insert() takes a JsObject as parameter, or anything that can be
* turned into a JsObject using a Writes.)
*/
val transformer: Reads[JsObject] =
Reads.jsPickBranch[JsString](__ \ "firstName") and
Reads.jsPickBranch[JsString](__ \ "lastName") and
Reads.jsPickBranch[JsNumber](__ \ "age") reduce
request.body.transform(transformer).map { result =>
collection.insert(result).map { lastError =>
Logger.debug(s"Successfully inserted with LastError: $lastError")
Created
}
}.getOrElse(Future.successful(BadRequest("invalid json")))
}
def findByName(name: String) = Action.async {
// let's do our query
val cursor: Cursor[JsObject] = collection.
// find all people with name `name`
find(Json.obj("name" -> name)).
// sort them by creation date
sort(Json.obj("created" -> -1)).
// perform the query and get a cursor of JsObject
cursor[JsObject](ReadPreference.primary)
// gather all the JsObjects in a list
val futurePersonsList: Future[List[JsObject]] = cursor.collect[List]()
// transform the list into a JsArray
val futurePersonsJsonArray: Future[JsArray] =
futurePersonsList.map { persons => Json.arr(persons) }
// everything's ok! Let's reply with the array
futurePersonsJsonArray.map { broker =>
Ok(broker)
}
}
}
My routes is:
# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~
# An example controller showing a sample home page
GET / controllers.HomeController.index
GET /crear/:name/:age controllers.UserController.create(name: String, age: Int)
GET /buscar/:name controllers.UserController.findByName(name: String)
# Map static resources from the /public folder to the /assets URL path
GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset)
I am currently building a REST Api using Play 2.5 (Scala) using Play Silhouette 4.0 as my authentication library.
I have a need to invalidate JWT per user therefore I will need to persist the authenticator in the database (mongodb). I am using ReactiveMongo 0.12.1.
Following the code examples of Silhouette's CacheAuthenticatorRepository and MongoAuthInfoDAO for ReactiveMongo, I have created an authenticator repository just for the JWT Authenticator like so:
package modules
import javax.inject.Inject
import com.mohiva.play.silhouette.api.StorableAuthenticator
import com.mohiva.play.silhouette.api.repositories.AuthenticatorRepository
import modules._
import com.mohiva.play.silhouette.api.util._
import scala.concurrent.Future
import scala.concurrent.duration.Duration
import scala.reflect.ClassTag
import com.mohiva.play.silhouette.api.{ AuthInfo, LoginInfo }
import com.mohiva.play.silhouette.persistence.exceptions.MongoException
import play.api.Configuration
import play.api.libs.concurrent.Execution.Implicits._
import play.api.libs.json.{ Format, JsObject, Json }
import play.modules.reactivemongo.ReactiveMongoApi
import play.modules.reactivemongo.json._
import reactivemongo.api.commands.WriteResult
import reactivemongo.play.json.collection.JSONCollection
import play.api.libs.json.Reads
/**
* Implementation of the authenticator repository which uses the reactive mongo to persist the authenticator.
*
* #param cacheLayer The cache layer implementation.
* #tparam T The type of the authenticator to store.
*/
class MongoAuthenticatorRepository[JWTAuthenticator] #Inject() (reactiveMongoApi: ReactiveMongoApi)
extends AuthenticatorRepository[JWTAuthenticator] {
def collection: Future[JSONCollection] = reactiveMongoApi.database.map(_.collection("jwt.auth.repo"))
/**
* Finds the authenticator for the given ID.
*
* #param id The authenticator ID.
* #return The found authenticator or None if no authenticator could be found for the given ID.
*/
override def find(id: String): Future[Option[JWTAuthenticator]] = {
//cacheLayer.find[JWTAuthenticator](id)
val query = Json.obj("id" -> id)
collection.flatMap(_.find(query).one[JWTAuthenticator])
}
/**
* Adds a new authenticator.
*
* #param authenticator JWTAuthenticatorhe authenticator to add.
* #return JWTAuthenticatorhe added authenticator.
*/
override def add(authenticator: JWTAuthenticator): Future[JWTAuthenticator] = {
//cacheLayer.save[JWTAuthenticator](authenticator.id, authenticator, Duration.Inf)
val obj = Json.obj("id" -> authenticator.id , "authenticator" -> authenticator, "duration" -> Duration.Inf)
collection.flatMap(_.insert(obj))
}
/**
* Updates an already existing authenticator.
*
* #param authenticator JWTAuthenticatorhe authenticator to update.
* #return JWTAuthenticatorhe updated authenticator.
*/
override def update(authenticator: JWTAuthenticator): Future[JWTAuthenticator] = {
//cacheLayer.save[JWTAuthenticator](authenticator.id, authenticator, Duration.Inf)
val obj = Json.obj("id" -> authenticator.id , "authenticator" -> authenticator, "duration" -> Duration.Inf)
collection.flatMap(_.update(Json.obj("id" -> authenticator.id), obj, upsert = true))
}
/**
* Removes the authenticator for the given ID.
*
* #param id JWTAuthenticatorhe authenticator ID.
* #return An empty future.
*/
override def remove(id: String): Future[Unit] = {
//cacheLayer.remove(id)
val query = Json.obj("id" -> id)
collection.flatMap(_.remove(query))
}
}
I then implemented it by modifying my silhouette.module file like so:
#Provides def provideAuthenticatorService(authenticatorEncoder: AuthenticatorEncoder, idGenerator: IDGenerator, configuration: Configuration, clock: Clock): AuthenticatorService[JWTAuthenticator] = {
val config = new JWTAuthenticatorSettings(
fieldName = configuration.underlying.getString("silhouette.authenticator.fieldName"),
//requestParts = Some(configuration.underlying.getAs[Seq[RequestPart.Value]]("silhouette.authenticator.requestParts").get),
issuerClaim = configuration.underlying.getString("silhouette.authenticator.issuerClaim"),
sharedSecret = configuration.underlying.getString("silhouette.authenticator.sharedSecret"),
authenticatorExpiry = configuration.underlying.getAs[FiniteDuration]("silhouette.authenticator.authenticatorExpiry").get,
authenticatorIdleTimeout = Some(configuration.underlying.getAs[FiniteDuration]("silhouette.authenticator.authenticatorIdleTimeout").get)
)
implicit lazy val format = Json.format[JWTAuthenticator]
val repo = new MongoAuthenticatorRepository[JWTAuthenticator]
new JWTAuthenticatorService(config, repo, authenticatorEncoder, idGenerator, clock)
}
I keep having problems compiling the code and the errors shown are:
"No Json deserializer found for type JWTAuthenticator. Try to implement an implicit Reads or Format for this type."
and
"Imported `MongoAuthenticatorRepository' is permanently hidden by definition of class MongoAuthenticatorRepository in package modules"
I tried to implement a custom JWTAuthenticator class as well and setting implicit formats within the companion object. However the errors still persists.
I am new to Scala as well as Play Framework, would appreciate any pointers or tips on resolving the above issue! Thanks!!
I have the following case class and class in Play Framework 2.5. Since the DB object has been deprecated I'm trying to use the new dependency inject methodology but not understanding why my code does not compile. The old method works fine, the new method has compilation error.
missing parameter type on the implicit connection =>
package models
import javax.inject.{Inject, Singleton}
import play.db._
import anorm.SqlParser._
import anorm._
import play.api.db.DB
import play.api.Play.current
case class Department(id: Int,
name: String)
#Singleton
class DepartmentDao #Inject() (dBApi: DBApi) {
private val db = dBApi.getDatabase("default")
val simple = {
get[Int]("department.id") ~
get[String]("department.name") map {
case id ~ name => Department(id, name)
}
}
def getDepartments = DB.withConnection { implicit connection =>
SQL("SELECT * FROM DEPARTMENT")
}
def getDepartmentsNew = db.withConnection { implicit connection =>
SQL("SELECT * FROM DEPARMTMENT")
}
}
UPDATE:
I've placed what the code looked like before was I was originally trying to get it working. Unless I am missing something this code should run. It is identical to code I've seen in multiple sample projects.
#Singleton
class DepartmentDao #Inject() (dBApi: DBApi) {
private val db = dBApi.getDatabase("default")
val simple = {
get[Int]("department.id") ~
get[String]("department.name") map {
case id ~ name => Department(id, name)
}
}
def getDepartments: Seq[Department] = db.withConnection { implicit connection =>
SQL("SELECT * FROM DEPARMTMENT").as(simple.*)
}
}
I'm doing tests with reactivemongo
In my controller I have this:
package controllers
import models._
import models.JsonFormats._
import play.modules.reactivemongo.MongoController
import scala.concurrent.Future
import reactivemongo.api.Cursor
import org.slf4j.{LoggerFactory, Logger}
import javax.inject.Singleton
import play.api.mvc._
import reactivemongo.api.collections.default.BSONCollection
import reactivemongo.bson._
#Singleton
class Users extends Controller with MongoController {
private final val logger: Logger = LoggerFactory.getLogger(classOf[Users])
val collection = db[BSONCollection]("users")
// list all articles and sort them
def list = Action.async { implicit request =>
// get a sort document (see getSort method for more information)
val sort = getSort(request)
// build a selection document with an empty query and a sort subdocument ('$orderby')
val query = BSONDocument(
"$orderby" -> sort,
"$query" -> BSONDocument())
val activeSort = request.queryString.get("sort").flatMap(_.headOption).getOrElse("none")
// the cursor of documents
val found = collection.find(query).cursor[User]
// build (asynchronously) a list containing all the articles
found.collect[List]().map { users =>
Ok(views.html.admin.list(users, activeSort))
}.recover {
case e =>
e.printStackTrace()
BadRequest(e.getMessage())
}
}
...........
}
and in my model i have this:
package models
import reactivemongo.bson._
case class User(
nickName: String,
email: String,
password: String,
active: Boolean
)
object JsonFormats {
import play.api.libs.json.Json
// Generates Writes and Reads for Feed and User thanks to Json Macros
implicit val userFormat = Json.format[User]
}
When I compile the project returns the following error:
could not find implicit value for parameter reader: reactivemongo.bson.BSONDocumentReader[models.User]
in this line is the problem:
val found = collection.find(query).cursor[User]
Can anyone tell me where I'm wrong or what I'm missing please?
You have no implicit handler defined to map your model class to a BSONDocument. You can implement it yourself, or, just like you did for the JsonFormats, you could use the macros provided by ReactiveMongo.
object BsonFormats {
import reactivemongo.bson.Macros
implicit val userFormat = Macros.handler[User]
}
Alternatively, instead of the BSONCollection, you could use the JSONCollection provided by Play-ReactiveMongo to perform your mapping using the JSON format that you have already defined.
For me, I still get the error even after I have declared the implicits for both bson and json format. What I need to do is just import this:
import reactivemongo.api.commands.bson.BSONCountCommandImplicits._