How to handle circular dependency when using PlayFramework and Guice? - scala

I'm using PlayFramework 2.6 with Scala and Guice and I have the following design between my components:
Some Person class objects:
#Singleton
class Person1 #Inject() (personApiService: PersonApiService, personDBApiService: PersonDBApiService) extends Person {
override def gerPersonInfo(person: Person): Future[PersonInfo] = {
...
}
}
I have few of those (Person1, Person2...).
In PersonService I need a list of the people instances so I did this:
Created a trait:
trait PeopleManager {
def application: Application
def peopleList = List(
classOf[Person1],
classOf[Person2],
...
)
lazy val peopleInstances: List[Person] = peopleList.map(personClass => application.injector.instanceOf(personClass))
}
So now I can do:
class PersonService #Inject() (val application: Application) extends PeopleManager {
// now I can use here peopleInstances.
}
The problem is, in my controller I get PersonService as dependency and I get to circular dependency. So in my controller I have to use Provider as suggested by PlayFramework documentation:
class MyController #Inject() (cc: ControllerComponents, peopleService: Provider[PersonServcie]) extends AbstractController(cc) {
...
}
This solved the circular dependency issue, but I have a problem now in my tests to create a mock for Provider[PersonService].
I feel something can be better in the design here that wont make me get circular dependency error, does someone have a nice suggestion?

Related

Is it possible to use scala self types with guice?

I have a controller:
class HomeController #Inject() (cc: ControllerComponents) extends AbstractController(cc)with Logging
{
this: SecuredActionByToken =>
def index = CheckedToken{ ...
Where SecuredActionByToken trait is using class names CheckTokenService to verify that user can run 'index' (we are trying to move from kind of cake pattern to Guice).
Is it possible to inject CheckTokenService to SecuredActionByToken? Of course, I could inject it to HomeController itself and work somehow with it, but I don't really want to push some services to controllers when they don't directly use them.
i ended up creating my own provider for this bean, this way I can inject service directly to provider and use it in overriden trait method:
class HomeControllerFactory #Inject()
(controllerComponents: ControllerComponents, cts: CheckTokenService, dbConfigProvider: DatabaseConfigProvider)
extends Provider[HomeController]{
override def get(): HomeController = {
new HomeController(controllerComponents) with SecuredActionByToken {
override def checkTokenService: CheckTokenService = cts
}
}
}
plus binding:
bind(classOf[HomeController]).toProvider(classOf[HomeControllerFactory])
I can also test is by creating homeController like this:
val testCheckTokenService =...
val homeController = new HomeController(stubControllerComponents()) with SecuredActionByToken {
override def checkTokenService: CheckTokenService = testCheckTokenService....
}

Inject a list of objects to play application context

In my project I have bunch of animal objects, for example:
some of them have dependency injection:
class Monkey #Inject() (wsClient: WSClient, configuration: Configuration) extends Animal {
...
}
and some not:
class Giraffe extends Animal {
...
}
In my AnimalsService class I need a list of all the animal objects instances,
Currently my service is getting the list of people as a dependency injection:
class AnimalsService #Inject() (animals: List[Animal]) {
// here I can use animals as my desire
}
and then I have a binding class that bind it:
class Bindings extends AbstractModule {
override def configure(): Unit = {
bind(classOf[AnimalsService]).toProvider(classOf[AnimalServiceProvider])
}
}
object Bindings {
class AnimalServiceProvider #Inject () (giraffe: Giraffe, monkey: Monkey ...) extends Provider[AnimalsService] {
override def get: AnimalsService = {
new AnimalsService(List(giraffe,monkey...))
}
}
}
This works perfectly, but what I would prefer is to have somehow to add the list to my application context as the app loads so I don't need to do it this way....
This current solution also means I need to add new animals to AnimalServiceProvider constructor and to here new AnimalsService(List(giraffe,monkey...)) every time I need a new animal, and that will be happened constantly...
What will be the best way of handling this kind of situation?
I thought maybe using #Named annotation of guice but not sure if its the right way or how to name a list of objects this way...
I would do a Singleton bean that has the list and a method to add definitions
class AnimalsService #Inject() () {
val animals: List[Animal]
def addAnimal(animal: Animal) {
....
}
// here I can use animals as my desire
}
Then in each module that needs an animal would need an extra singleton with Eager bindings
class MonkeyConfigurator #Inject() (animalsService: AnimalsService) extends Animal {
animalsService.add(this) //Or anything
// here I can use animals as my desire
}

Play framework inherit dependency injected trait?

Is it possible to create an overloaded play.api.mvc.Controller trait that has dependency injected arguments?
For example, say I have a couple of customized Actions that require a dependency injected AuthorizationService. I would like to write my controllers like this:
class UserController extends CustomController {
def getUser(userID: String) = CustomAction {
...
}
}
However, I can't figure out how to create the CustomController trait such that it doesn't require me to inject my AuthorizationService in my UserController. Is there a way to do this with Guice?
You can inject a field into your CustomController trait. The field should'n be final so it has to be declared as var in Scala.
#Inject() var authService: AuthorizationService
You can also make the injected var private and declare a public val which references the injected field. In this case val has to be lazy since injection occurs after class was instantiated. See Guice docs for more details.
#Inject() private var as: AuthorizationService = _
lazy val authService: AuthorizationService = as
It isn't possible to inject a dependency into a trait because a trait isn't instantiable. A trait doesn't have a constructor to define the dependencies, you must inject your AuthService via UserController
Example.
trait CustomController extends Controller {
val authService: AuthService
...
}
class UserController #Inject()(override val authService: AuthService) extends CustomController {
...
}

Guice in Scala: Module for a class that has a DI-constructor itself

I'm using codingwell/scala-guice and trying to inject DAO-classes into constructors of other components/classes.
In the first attempt, I only used one DAO-class to see if it works:
class DaoModule extends AbstractModule with ScalaModule {
override def configure() {
val dao1 = new FirstDaoImpl
bind(new TypeLiteral[FirstDaoTrait] {}).toInstance(dao1)
}
}
The binding works as expected, it can be used for constructor injection.
In the second step, I wanted to add another DAO class to the module. However, that DAO-class depends on the first DAO:
class SecondDaoImpl #Inject()(firstDao: FirstDaoTrait) extends SecondDaoTrait
I'm not sure how to add the necessary binding to the existing module. Repeating the first step would result in this:
val dao2 = new SecondDaoImpl(???)
bind(new TypeLiteral[SecondDaoTrait] {}).toInstance(dao2)
But of course this class can only be instantiated by providing the first DAO (therefore the "???"). How can I do this?
Use bind and let scala-guice resolve the dependencies for you:
class DaoModule extends AbstractModule with ScalaModule {
override def configure() {
bind[FirstDaoTrait].to[FirstDaoImpl]
bind[SecondDaoTrait].to[SecondDaoImpl]
}
}
And now using the injector:
val injector = Guice.createInjector(new DaoModule())
val secondDao = injector.instance[SecondDaoTrait]

Circular Dependency Error for Google Guice with Play2.4 and scala

My application uses Play 2.4 with Scala 2.11 .I started transforming my existing code to make use of Google Guice that comes with Play 2.4 .
When I run my code after making the first set of changes , I found Some DAOs in my code are failing with circular dependency error.
For example I have two DAOs
class BookDAO #Inject()
(protected val personDAO : PersonDAO,
#NamedDatabase("mysql")protected val dbConfigProvider: DatabaseConfigProvider) extends HasDatabaseConfigProvider[JdbcProfile] {
...
...
val personId = //some id
personDAO.get(personId)
}
class PersonDAO #Inject()
(protected val bookDAO : BookDAO,
#NamedDatabase("mysql")protected val dbConfigProvider: DatabaseConfigProvider) extends HasDatabaseConfigProvider[JdbcProfile] {
...
...
val bookName= //some id
personDAO.getByName(bookName)
}
I got the below error while trying to access either BookDAO or PersonDAO
Tried proxying schema.BookDAO to support a circular dependency, but it is not an interface.
at schema.BookDAO.class(Books.scala:52)
while locating schema.BookDAO
Can someone help me resolving this error .
Thanks in advance
Quick solution
Inject a Provider instead:
class BookDAO #Inject()(personDaoProvider: Provider[PersonDAO], ...)
extends HasDatabaseConfigProvider[JdbcProfile] {
val personDAO = personDaoProvider.get
def person = personDAO.get(personId)
}
And the same for BookDAO. This will work out of the box. Guice already "knows" how to inject Providers.
Better approach
Decouple the class definition from the implementation. See Mon Calamari's answer.
Define your dependencies as follows and pull up all needed methods from class to trait:
#ImplementedBy(classOf[BookDao])
trait IBookDao {
// abstract defs
}
class BookDao #Inject()(protected val personDAO: IPersonDao, protected val dbConfigProvider: DatabaseConfigProvider) extends HasDatabaseConfigProvider[JdbcProfile] with IBookDao {
}
#ImplementedBy(classOf[PersonDao])
trait IPersonDao {
// abstract defs
}
class PersonDao #Inject()(protected val bookDAO: IBookDao, protected val dbConfigProvider: DatabaseConfigProvider) extends HasDatabaseConfigProvider[JdbcProfile] with IPersonDao {
}
As you can see, each dao implements a trait and all dao dependencies are injected by trait. This gives Guice possibility to inject a proxy class and resolve a circular dependency issue.
More details on playframework scala dependency injection here.