greyfox
greyfox

Reputation: 6606

Play Framework Inject Error

I am trying to inject the JPAApi into my controller using Play 2.5 but I keep getting the following exception.

com.google.inject.ProvisionException: Unable to provision, see the following errors:

1) Error injecting constructor, java.lang.NoClassDefFoundError: org/dom4j/io/STAXEventReader
  at play.db.jpa.DefaultJPAApi$JPAApiProvider.<init>(DefaultJPAApi.java:39)
  at play.db.jpa.DefaultJPAApi$JPAApiProvider.class(DefaultJPAApi.java:34)
  while locating play.db.jpa.DefaultJPAApi$JPAApiProvider
  while locating play.db.jpa.JPAApi
    for parameter 0 at controllers.HomeController.<init>(HomeController.java:20)
  while locating controllers.HomeController
    for parameter 1 at router.Routes.<init>(Routes.scala:40)
  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

1 error
     com.google.inject.internal.InjectorImpl$2.get(InjectorImpl.java:1025)
     com.google.inject.internal.InjectorImpl.getInstance(InjectorImpl.java:1051)
     play.api.inject.guice.GuiceInjector.instanceOf(GuiceInjectorBuilder.scala:405)
     play.api.inject.guice.GuiceInjector.instanceOf(GuiceInjectorBuilder.scala:400)
     play.api.inject.guice.GuiceApplicationBuilder.build(GuiceApplicationBuilder.scala:123)
     play.api.inject.guice.GuiceApplicationLoader.load(GuiceApplicationLoader.scala:21)
     play.core.server.DevServerStart$$anonfun$mainDev$1$$anon$1$$anonfun$get$1$$anonfun$apply$1$$anonfun$1$$anonfun$2.apply(DevServerStart.scala:158)
     play.core.server.DevServerStart$$anonfun$mainDev$1$$anon$1$$anonfun$get$1$$anonfun$apply$1$$anonfun$1$$anonfun$2.apply(DevServerStart.scala:155)
     play.utils.Threads$.withContextClassLoader(Threads.scala:21)
     play.core.server.DevServerStart$$anonfun$mainDev$1$$anon$1$$anonfun$get$1$$anonfun$apply$1$$anonfun$1.apply(DevServerStart.scala:155)
     play.core.server.DevServerStart$$anonfun$mainDev$1$$anon$1$$anonfun$get$1$$anonfun$apply$1$$anonfun$1.apply(DevServerStart.scala:126)
     scala.Option.map(Option.scala:146)
     play.core.server.DevServerStart$$anonfun$mainDev$1$$anon$1$$anonfun$get$1$$anonfun$apply$1.apply(DevServerStart.scala:126)
     play.core.server.DevServerStart$$anonfun$mainDev$1$$anon$1$$anonfun$get$1$$anonfun$apply$1.apply(DevServerStart.scala:124)
     scala.util.Success.flatMap(Try.scala:231)
     play.core.server.DevServerStart$$anonfun$mainDev$1$$anon$1$$anonfun$get$1.apply(DevServerStart.scala:124)
     play.core.server.DevServerStart$$anonfun$mainDev$1$$anon$1$$anonfun$get$1.apply(DevServerStart.scala:116)
     scala.concurrent.impl.Future$PromiseCompletingRunnable.liftedTree1$1(Future.scala:24)
     scala.concurrent.impl.Future$PromiseCompletingRunnable.run(Future.scala:24)
     java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1402)
     java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
     java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
     java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
     java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)

This is my controller.

public class HomeController extends Controller {

    private JPAApi jpaApi;

    @Inject
    public HomeController(JPAApi jpaApi) {
        this.jpaApi = jpaApi;
    }

    public Result index() {
        jpaApi.withTransaction(entityManager -> {
            Query query = entityManager.createNativeQuery("select max(age) from people");
            return (Long) query.getSingleResult();
        });
        return ok(index.render("Your new application is ready."));
    }

}

Upvotes: 1

Views: 2735

Answers (2)

mkurz
mkurz

Reputation: 2826

You just bumped into a bug in sbt (the build tool Play is using). This bug arrises with Hibernate version 5.2.1 (but not with 5.2.0).

Hibernate 5.2.1 started do exclude all transitive dependencies of dom4j with a maven syntax that sbt can't handle yet.

The workaround for now is to add

"dom4j" % "dom4j" % "1.6.1" intransitive()

to your libraryDependencies in build.sbt (in parallel to the Hibernate dependency).

For more detailed information have a look at the sbt bug itself which can be found here: https://github.com/sbt/sbt/issues/1431

The Hibernate bug I reported (but has been closed as it turned out to be a sbt issue) can be found here: https://hibernate.atlassian.net/browse/HHH-10916

Upvotes: 9

homerman
homerman

Reputation: 3569

based on the small snippet you've provided i'm running on the assumption that you're using the Play Java API. assuming as much, i'd first recommend you refer to the Play documentation on dependency injection.

if you've bootstrapped your project using one of the Activator templates, you probably ended up with the following two files (among many others):

.
├── app
│   ├── Module.java
└── conf
    └── application.conf

"Modules" are one way to configure bindings in Guice (basically what Guice will serve back when a component requests injection of a given type).

in the interest of getting you up and running in the meantime...

...first, in the application.conf file under the play.modules section, uncomment the line to add to the enabled set of modules, so you'll end up with something like this:

play.modules {
    enabled += Module
}

...next, assuming you have an interface named JPAApi with a concrete implementation JPAApiImpl... update the Module.java as follows:

import com.google.inject.AbstractModule;

public class Module extends AbstractModule {
    @Override
    public void configure() {
        bind(JPAApi.class).to(JPAApiImpl.class);
    }
}

(or if JPAApi is a concrete implementation itself, you could just bind that to itself)

restart your application and your controller should be injected.

Upvotes: 0

Related Questions