1
votes

I am trying to deal with dependency injected objects in Scala template (I am using Java-based Play 2.5).

I have a system of templates, where I have layout template with minimal HTML base and that one is included by almost all other HTML templates which are constructing the rest of the HTML body.

In the template, I am also including top menu with the "Logout" button and also there is a name of currently logged user.

I have a singleton object called LocalAuthenticator which is delivering the User object containing username. So far I was using dependency injection using helper Scala object like this

object LocalAuthenticator {   
  private val cache = Application.instanceCache[core.security.LocalAuthenticator]

  object Implicits {
    implicit def localAuth(implicit application: Application): core.security.LocalAuthenticator = cache(application)   } 
  }

Then I was able to access LocalAuthenticator from template using this construct

@import play.api.Play.current
@import scala.LocalAuthenticator.Implicits._

Logged in user is: @localAuth.getCurrentUser().name

This was working in 2.4, however 2.5 is complaining about play.api.Play.current to be deprecated as it uses static context.

I know that the correct approach is to inject LocalAuthenticator to Controller and pass it to template, however this object should be present in all templates and it is very annoying to inject it to every controller.

Is there any way how to inject a singleton class into template directly?

I was trying to get injector in helper object in order to get singleton, but that could be done only if I have Play Application object. And that could be retrieved only using DI. So I am running in circles :-)

2

2 Answers

1
votes

Allright, found a solution (or a workaround maybe).

Using this page https://github.com/google/guice/wiki/Injections#static-injections I created a helper module loaded at application startup.

public class DIStaticModule extends AbstractModule {
    protected void configure() {
        requestStaticInjection(DIStaticFactory.class);
    }
}

... and then a factory class ...

public class DIStaticFactory {
    @Inject
    static LocalAuthenticator localAuthenticator;
    @Inject
    static LocalMessagingService localMessagingService;
    @Inject
    static OperationsMessagingService operationsMessagingService;

    public static LocalAuthenticator getLocalAuthenticator() {
        return localAuthenticator;
    }

    public static LocalMessagingService getLocalMessagingService() {
        return localMessagingService;
    }

    public static OperationsMessagingService getOperationsMessagingService() {
        return operationsMessagingService;
    }
}

For a simple use I also created Scala object in order to have implicit variables directly in templates.

package core.di.scala

object DIStaticFactory {
  implicit val operationsMessaging: OperationsMessagingService = core.di.DIStaticFactory.getOperationsMessagingService
  implicit val localAuth: LocalAuthenticator = core.di.DIStaticFactory.getLocalAuthenticator
  implicit val localMessaging: LocalMessagingService = core.di.DIStaticFactory.getLocalMessagingService
}

Static variables are injected on Guice injector startup as stated in Guice docs. Everything is working as expected.

Usage in Scala templates is simple ...

@import core.di.scala.DIStaticFactory._

@if(localAuth.getCurrentUser().hasPermission("debug.tweaker")) { .. do something ... }

Do not forget to activate module in application.conf

0
votes

I think you can try to use ActionBuilder + implicit parameters on view to achieve your requirement. (My answer is based on the link on my comment).

First you need to define an ActionBuild that extract the current user from database or from session object field on request and add it to a subtype of WrappedRequest.

//The subtype of WrapperRequest which now contains the username. 
class UserRequest[A](val username: Option[String], request: Request[A]) extends WrappedRequest[A](request)

object UserAction extends
  ActionBuilder[UserRequest] with ActionTransformer[Request, UserRequest] {
  def transform[A](request: Request[A]) = Future.successful {
    //request.session.get("username") in this example is the code to get the current user
    //you can do db access here or other means. 
    new UserRequest(request.session.get("username"), request)
  }
}

On you view you will define an implicit parameter of type UserRequest:

(message: String)(implicit h: UserRequest[AnyContent])

@main("Welcome to Play") {
   //DISPLAY h.username
   @h.username
}

Lastly, on your Controller define a requestHandler that uses UserAction which implicify it's request object.

// implicit user: UserRequest does the magic
def index = UserAction { implicit user: UserRequest[AnyContent] =>
    Ok(views.html.index("Not Yet Implemented."))
}

Hope that's help.

More on ActionBuilder: https://www.playframework.com/documentation/2.5.x/ScalaActionsComposition