ClassCastException in TemplateRouter.scala when rendering a Scala HTML Template

Hi Play Experts,

We are getting a ClassCastException in our TemplateRouter.scala file when rendering a Scala HTML Template using the Twirl API.

This is in Play 2.7

This is not called from a Java controller, but instead, from a URL on a page in a REST call in our routes file. HttpSessionUtils is a Java POJO that we pass along that has some utility methods to be used by the templates. Does anyone know what we should be doing here? Or does anyone know of a sample out there that we could take a look at? We haven’t been able to find one on Github or via Google.

Here is the TemplateRouter code:

package common

import play.twirl.api._

object TemplateRouter {

    def renderTemplate(name: String) = {
        val clazz = Class.forName("views.html.includes.%s$".format(name))
        val template = clazz.getField("MODULE$").get(null).asInstanceOf[play.twirl.api.Template0[play.twirl.api.Html]]
        template.render()
    }

    def renderTemplate(name: String, arg0: Any) = {
        Console.println("name: " + name + "  arg: " + arg0);
        val clazz = Class.forName("views.html.includes.%s$".format(name))
        val template = clazz.getField("MODULE$").get(null).asInstanceOf[play.twirl.api.Template1[Any, play.twirl.api.Html]]
        template.render(arg0)
    }

    def renderTemplate(name: String, arg0: Any, arg1: Any) = {
        val clazz = Class.forName("views.html.includes.%s$".format(name))
        val template = clazz.getField("MODULE$").get(null).asInstanceOf[play.twirl.api.Template2[Any, Any, play.twirl.api.Html]]
        template.render(arg0, arg1)
    }
}

Here is a snippet of the Scala HTML Template:

@(widget: models.DefaultWidget, httpSessionUtils: controllers.HttpSessionUtils)(implicit request: Http.Request)
@import models.Student; var student = httpSessionUtils.getCurrentUser(request).getLastStudent();

<script>
angular.module('learnerDetails', ['mm.foundation','ngSanitize'])
. . .
</script>
<html>
. . .
</html>

Here is the error:

[ClassCastException: class views.html.includes.students.learnerDetails$ cannot be cast to class play.twirl.api.Template1 (views.html.includes.students.learnerDetails$ is in unnamed module of loader play.runsupport.DelegatedResourcesClassLoader @53b87c68; play.twirl.api.Template1 is in unnamed module of loader play.runsupport.NamedURLClassLoader @2416430e)]

Here is the stack trace:

Thu 19:57:18.955 ERROR application 

! @7d4o8lk32 - Internal server error, for (GET) [/dash/9/Dashboard] ->
 
play.api.http.HttpErrorHandlerExceptions$$anon$1: Execution exception[[ClassCastException: class views.html.includes.students.learnerDetails$ cannot be cast to class play.twirl.api.Template1 (views.html.includes.students.learnerDetails$ is in unnamed module of loader play.runsupport.DelegatedResourcesClassLoader @53b87c68; play.twirl.api.Template1 is in unnamed module of loader play.runsupport.NamedURLClassLoader @2416430e)]]
	at play.api.http.HttpErrorHandlerExceptions$.throwableToUsefulException(HttpErrorHandler.scala:351)
	at play.api.http.DefaultHttpErrorHandler.onServerError(HttpErrorHandler.scala:267)
	at play.core.server.AkkaHttpServer$$anonfun$1.applyOrElse(AkkaHttpServer.scala:448)
	at play.core.server.AkkaHttpServer$$anonfun$1.applyOrElse(AkkaHttpServer.scala:446)
	at scala.concurrent.Future.$anonfun$recoverWith$1(Future.scala:417)
	at scala.concurrent.impl.Promise.$anonfun$transformWith$1(Promise.scala:41)
	at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:64)
	at akka.dispatch.BatchingExecutor$AbstractBatch.processBatch(BatchingExecutor.scala:55)
	at akka.dispatch.BatchingExecutor$BlockableBatch.$anonfun$run$1(BatchingExecutor.scala:92)
	at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23)
	at scala.concurrent.BlockContext$.withBlockContext(BlockContext.scala:85)
	at akka.dispatch.BatchingExecutor$BlockableBatch.run(BatchingExecutor.scala:92)
	at akka.dispatch.TaskInvocation.run(AbstractDispatcher.scala:41)
	at akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask.exec(ForkJoinExecutorConfigurator.scala:49)
	at akka.dispatch.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
	at akka.dispatch.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
	at akka.dispatch.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
	at akka.dispatch.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)
Caused by: java.lang.ClassCastException: class views.html.includes.students.learnerDetails$ cannot be cast to class play.twirl.api.Template1 (views.html.includes.students.learnerDetails$ is in unnamed module of loader play.runsupport.DelegatedResourcesClassLoader @53b87c68; play.twirl.api.Template1 is in unnamed module of loader play.runsupport.NamedURLClassLoader @2416430e)
	at common.TemplateRouter$.renderTemplate(TemplateRouter.scala:19)
	at views.html.includes.defaultWidget$.apply(defaultWidget.template.scala:40)
	at views.html.includes.defaultWidget$.render(defaultWidget.template.scala:53)
	at views.html.includes.defaultWidget.render(defaultWidget.template.scala)
	at services.widget.WidgetServiceImpl.getHtmlView(WidgetServiceImpl.java:44)
	at controllers.Application.lambda$dash$30(Application.java:1755)
	at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1654)
	at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:658)
	at controllers.Application.dash(Application.java:1749)
	at router.Routes$$anonfun$routes$1.$anonfun$applyOrElse$1272(Routes.scala:13154)
	at play.core.routing.HandlerInvokerFactory$$anon$9.resultCall(HandlerInvoker.scala:155)
	at play.core.routing.HandlerInvokerFactory$JavaActionInvokerFactory$$anon$3$$anon$4$$anon$5.invocation(HandlerInvoker.scala:116)
	at play.core.j.JavaAction$$anon$1.call(JavaAction.scala:131)
	at play.mvc.Action.lambda$call$0(Action.java:89)
	at java.base/java.util.Optional.map(Optional.java:265)
	at play.mvc.Action.call(Action.java:81)
	at play.http.DefaultActionCreator$1.call(DefaultActionCreator.java:33)
	at play.mvc.Security$AuthenticatedAction.lambda$call$2(Security.java:62)
	at java.base/java.util.Optional.map(Optional.java:265)
	at play.mvc.Security$AuthenticatedAction.call(Security.java:62)
	at play.core.j.JavaAction.$anonfun$apply$8(JavaAction.scala:191)
	at scala.concurrent.Future$.$anonfun$apply$1(Future.scala:658)
	at scala.util.Success.$anonfun$map$1(Try.scala:255)
	at scala.util.Success.map(Try.scala:213)
	at scala.concurrent.Future.$anonfun$map$1(Future.scala:292)
	at scala.concurrent.impl.Promise.liftedTree1$1(Promise.scala:33)
	at scala.concurrent.impl.Promise.$anonfun$transform$1(Promise.scala:33)
	at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:64)
	at play.core.j.HttpExecutionContext$$anon$2.run(HttpExecutionContext.scala:79)
	at play.api.libs.streams.Execution$trampoline$.execute(Execution.scala:72)
	at play.core.j.HttpExecutionContext.execute(HttpExecutionContext.scala:71)
	at scala.concurrent.impl.CallbackRunnable.executeWithValue(Promise.scala:72)
	at scala.concurrent.impl.Promise$KeptPromise$Kept.onComplete(Promise.scala:372)
	at scala.concurrent.impl.Promise$KeptPromise$Kept.onComplete$(Promise.scala:371)
	at scala.concurrent.impl.Promise$KeptPromise$Successful.onComplete(Promise.scala:379)
	at scala.concurrent.impl.Promise.transform(Promise.scala:33)
	at scala.concurrent.impl.Promise.transform$(Promise.scala:31)
	at scala.concurrent.impl.Promise$KeptPromise$Successful.transform(Promise.scala:379)
	at scala.concurrent.Future.map(Future.scala:292)
	at scala.concurrent.Future.map$(Future.scala:292)
	at scala.concurrent.impl.Promise$KeptPromise$Successful.map(Promise.scala:379)
	at scala.concurrent.Future$.apply(Future.scala:658)
	at play.core.j.JavaAction.apply(JavaAction.scala:192)
	at play.api.mvc.Action.$anonfun$apply$2(Action.scala:98)
	at play.api.libs.streams.StrictAccumulator.$anonfun$mapFuture$4(Accumulator.scala:184)
	at scala.util.Try$.apply(Try.scala:213)
	at play.api.libs.streams.StrictAccumulator.$anonfun$mapFuture$3(Accumulator.scala:184)
	at scala.Function1.$anonfun$andThen$1(Function1.scala:57)
	at scala.Function1.$anonfun$andThen$1(Function1.scala:57)
	at play.api.libs.streams.StrictAccumulator.run(Accumulator.scala:219)
	at play.core.server.AkkaHttpServer.$anonfun$runAction$4(AkkaHttpServer.scala:441)
	at akka.http.scaladsl.util.FastFuture$.strictTransform$1(FastFuture.scala:41)
	at akka.http.scaladsl.util.FastFuture$.$anonfun$transformWith$3(FastFuture.scala:51)
	... 12 common frames omitted

Hey @Kenye,

Don’t use Class.forName, but instead, use the application ClassLoader. You can access it through play.Environment.classLoader method.

By the way, this does not look like a good use of a compiled type-safe template engine such as Twirl. Why do you need to use reflection instead of directly using the template? It is also makes refactoring and renames more complicated since you won’t notice the problems at compile time.