0

I'm working on this Scala play-authenticate-usage-scala project where I build on top of a Java framework play-authenticate (I know, life is not perfect ...). While running the application I see the exception included below.

Carefully inspecting the stack trace leads me to the view views._providerPartial.scala.html that is using the forProviders.scala.html template that in turns uses classes that depend on Java's Play play.mvc.Http.{ Session, Context, etc} and thus, the error because my sample application brings the Scala play.api.mvc._ ones.

I know I can do in Scala:

import play.core.j.JavaHelpers

val jContext : play.mvc.Http.Context = JavaHelpers.createJavaContext(request)
val jSession : play.mvc.Http.Session = context.session() 

The question is how can I make these above implicitly available to that view.

[error] application - 

! @72ef8b5j8 - Internal server error, for (GET) [/login] ->

play.api.http.HttpErrorHandlerExceptions$$anon$1: Execution exception[[RuntimeException: There is no HTTP Context available from here.]]
    at play.api.http.HttpErrorHandlerExceptions$.throwableToUsefulException(HttpErrorHandler.scala:293)
    at play.api.http.DefaultHttpErrorHandler.onServerError(HttpErrorHandler.scala:220)
    at play.api.GlobalSettings$class.onError(GlobalSettings.scala:160)
    at play.api.DefaultGlobal$.onError(GlobalSettings.scala:188)
    at play.api.http.GlobalSettingsHttpErrorHandler.onServerError(HttpErrorHandler.scala:100)
    at play.core.server.netty.PlayRequestHandler$$anonfun$2$$anonfun$apply$1.applyOrElse(PlayRequestHandler.scala:100)
    at play.core.server.netty.PlayRequestHandler$$anonfun$2$$anonfun$apply$1.applyOrElse(PlayRequestHandler.scala:99)
    at scala.concurrent.Future$$anonfun$recoverWith$1.apply(Future.scala:346)
    at scala.concurrent.Future$$anonfun$recoverWith$1.apply(Future.scala:345)
    at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:32)
Caused by: java.lang.RuntimeException: There is no HTTP Context available from here.
    at play.mvc.Http$Context.current(Http.java:62)
    at play.mvc.Http$Context$Implicit.session(Http.java:330)
    at com.feth.play.module.pa.views.html.forProviders_Scope0$forProviders$$anonfun$apply$1.apply(forProviders.template.scala:37)
    at com.feth.play.module.pa.views.html.forProviders_Scope0$forProviders$$anonfun$apply$1.apply(forProviders.template.scala:35)
    at play.twirl.api.TemplateMagic$.defining(TemplateMagic.scala:13)
    at com.feth.play.module.pa.views.html.forProviders_Scope0$forProviders.apply(forProviders.template.scala:35)
    at views.html._providerPartial_Scope0$_providerPartial.apply(_providerPartial.template.scala:38)
    at views.html.login_Scope0$login_Scope1$login.apply(login.template.scala:74)
    at controllers.Application$$anonfun$4$$anonfun$apply$4.apply(Application.scala:57)
    at controllers.Application$$anonfun$4$$anonfun$apply$4.apply(Application.scala:57)

UPDATE I attempted the following but didn't work. Basically Java side expects all these in a ThreadLocal. The specific ThreadLocal instance is publicly located in play.mvc.Http.Context.current. Therefore I prepared a template helper called _adaptScalaToJava.scala.html like this:

@import be.objectify.deadbolt.scala._
@import play.core.j.JavaHelpers

@()(implicit request: AuthenticatedRequest[Any])

@play.mvc.Http.Context.current.set(JavaHelpers.createJavaContext(request))

it sets directly the jContext in the current thread and is used like this:

@_adaptScalaToJava
@forProviders(playAuth, skipCurrent) { p =>
    <li>
    @if(p.getKey() == "openid") {
        <a href="javascript:void(0);" onclick="askOpenID('@p.getUrl()');">
    } else {
        <a href="@p.getUrl()">
    }
    @_providerIcon(p.getKey())</a>
    </li>
}

but even though compiles find it still fires the same exception ...

SkyWalker
  • 13,729
  • 18
  • 91
  • 187

1 Answers1

0

Placing only this line before the troubling code solves the issue:

@play.mvc.Http.Context.current.set(play.core.j.JavaHelpers.createJavaContext(request))
@forProviders(playAuth, skipCurrent) { p =>

trying to make it a bit more clean i.e. that it isn't reset all the time, it is still a mystery for me why this won't work:

@if (play.mvc.Http.Context.current.get() == null) { 
    @play.mvc.Http.Context.current.set(play.core.j.JavaHelpers.
         createJavaContext(request)) 
} 

Seems like the play.mvc.Http.Context.current is not null ... bottom line is I need to connect with the IDE and debug the code.

UPDATE Actually this is the official way to do it:

@play.core.j.JavaHelpers.withContext(request) { jContext =>
    @forProviders(playAuth, skipCurrent) { p =>
        ...
    }
}
SkyWalker
  • 13,729
  • 18
  • 91
  • 187
  • 1
    That may work, but that's not the official way to do it -- JavaHelpers is not a part of Play's public API and may change without warning. – Will Sargent Dec 26 '16 at 00:58
  • and what is the official way to support Scala mixed Java <-> Scala apps? In my case I want to use Scala but can't live without a Java plugin – SkyWalker Dec 26 '16 at 01:04
  • 1
    You should set up a Java controller and then call out to Scala code from there – Will Sargent Dec 26 '16 at 02:52
  • Thank you for the comments, it is really useful! I have the app in a state that works but of course want it to be the best possible maintainance-wise ... good idea to implement those affected controllers into Java. Now after playing with Play for quite a while I still think that there should be better and more transparent support for mixed Scala-Java apps. I prefer to write Scala 10x more than Java and should not be forced by any framework to use a specific language. If play had a common base abstract layer and smooth implicit conversion there would not be need for a `play.core.j.JavaHelpers` – SkyWalker Dec 26 '16 at 10:07
  • I also think that there isn't any fundamental reason why the main play abstraction (Context, Request, Session, Flash, Messages, Form, etc) shouldn't share a common base or even be of the same type. I understand that one uses `implicit` while the other `ThreadLocal` to pass stuff around but still ...with so many plugins around done in either Java or Scala, we shouldn't be bumping into walls all the time. – SkyWalker Dec 26 '16 at 10:18
  • 1
    Refactoring this is a major part of Play 3.0, which is why I mention it. – Will Sargent Dec 26 '16 at 13:34
  • Excellent good to know! I isolate my use of the `play.core.j.JavaHelpers` to be ready for a smoother migration! Looking forward to it :) – SkyWalker Dec 26 '16 at 13:38