jerome
jerome

Reputation: 2089

Scala play Guice injection

I'm using scala play 2.5 and I have the following error while trying to inject an object in one of my controllers. I'm using the default injection framework given with play which is Guice.

    ProvisionException: Unable to provision, see the following errors: 
    1) No implementation for services.MyService was bound. 
    while locating services.MyService for parameter 0 at controllers.MyController.<init>(MyController.scala:12) 
    while locating controllers.MyController for parameter 3 at router.Routes.<init>(Routes.scala:55) 
    while locating router.Routes 
    while locating play.api.inject.RoutesProvider while locating play.api.routing.Router for parameter 0 at play.api.http.JavaCompatibleHttpRequestHandler.<init>(HttpRequestHandler.scala:200) 
    while locating play.api.http.JavaCompatibleHttpRequestHandler 
    while locating play.api.http.HttpRequestHandler for parameter 4 at play.api.DefaultApplication.<init>(Application.scala:221) at play.api.DefaultApplication.class(Application.scala:221) 
while locating play.api.DefaultApplication 
    while locating play.api.Application

Here is the controller:

package controllers

import services.MyService

class MyController @Inject()(myService: MyService, val messagesApi: MessagesApi) extends Controller with I18nSupport {

    def someFunctionThatUsesMyService(url: String) = Action {}
}

Here is the service I would like to inject:

package services

import javax.inject._

trait MyService {
    def op(param1: String, param2: String): Boolean
}

@Singleton
class BasicMyService extends MyService {
    override def op(param1: String, param2: String): Boolean = true
}

That is how I'm using it:

@Singleton
class HomeController @Inject() extends Controller {

  /**
   * Create an Action to render an HTML page with a welcome message.
   * The configuration in the `routes` file means that this method
   * will be called when the application receives a `GET` request with
   * a path of `/`.
   */
  def index = Action {
    //Ok(views.html.index("Your new application is ready."))
    Redirect(routes.MyController.someFunctionThatUsesMyService(Some(routes.OtherController.welcomePage().url)))
  }

}

Upvotes: 4

Views: 1102

Answers (2)

Capacytron
Capacytron

Reputation: 3729

can you try to inject services directly, why do you need that layer of dummy traits? All stuff must work out of the box w/o any extra coding. You can always fine-tune your external deps. See example below.

Here is some code

Controller:

class MyController @Inject()(service: SomeConcreteService) extends Controller { /** just use service.anyMethod here */ }

Service:

@Singleton
class SomeConcreteService @Inject()(otherStuff: OtherStuffConcrete){
/** otherStuff will be also injected */
    def mySuperServiceMethod:={ "Hey!" }
}

Test:

class MyControllerTest extends PlaySpec with OneAppPerSuite with MockitoSugar {

    // do it this way if you want framework to provide controller instance
    private val myController = app.injector.instanceOf[MyController]

    // or mock external deps and build controller on your own
    val externalService = mock[SomeConcreteService]
    val handMadeController = new MyController(externalService)

    "My controller" should{
        "do its best " in {
            val response = call(handMadeController.doStuff, FakeRequest())

            status(response) mustBe OK

            contentAsString(response) must include("Best controller")

    }
}

Upvotes: 0

mgosk
mgosk

Reputation: 1874

You should add ImplementedBy annotation to Service trait

package services

import javax.inject._

@ImplementedBy(classOf[BasicMyService])
trait MyService {
    def op(param1: String, param2: String): Boolean
}

@Singleton
class BasicMyService extends MyService {
    override def op(param1: String, param2: String): Boolean = true
}

Upvotes: 6

Related Questions