Correct way to integrate JAX-RS with CDI?
I used to integrate services and DAO beans in Jersey REST resources, annotating them with @Path
following Java EE tutorial
In general, for JAX-RS to work with beans corporation, you need to annotate the bean class with @Path to convert it to the root resource class. You can use @Path annotation with stateless session beans and singleton POJO beans.
So my code was something like this:
@Path("/")
public class ServiceResource {
@Inject
private AccountService accountService;
@GET
@Path("/account/get")
public Account getAccount(@QueryParam("id") String id) {
return accountService.get(id);
}
}
@javax.inject.Singleton
@Path("")
public class AccountService {
public Account get(String id){...}
}
Now I started integrating Quartz Job into my application and I wanted to find a way to insert mine AccountService
inside jobs like this
public class AccountJob implements Job {
@Inject
private AccountService accountService;
@Override
public void execute(JobExecutionContext jec) throws JobExecutionException {
accountService.updateAllAccounts();
}
}
I found this answer that says to use DeltaSpike
to get the job done, so I added the following dependencies to mine pom.xml
and don't add more line of code for any class, i.e. invalidation AccountService
for mine Job
works fine
<dependency>
<groupId>org.apache.deltaspike.modules</groupId>
<artifactId>deltaspike-scheduler-module-api</artifactId>
<version>1.7.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.deltaspike.modules</groupId>
<artifactId>deltaspike-scheduler-module-impl</artifactId>
<version>1.7.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.deltaspike.cdictrl</groupId>
<artifactId>deltaspike-cdictrl-api</artifactId>
<version>1.7.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.deltaspike.cdictrl</groupId>
<artifactId>deltaspike-cdictrl-weld</artifactId>
<version>1.7.2</version>
<scope>runtime</scope>
</dependency>
However , I realized that when I remove @Path("")
from AccountService
, its instance is still injected within within ServiceResource
, so I ask the following questions:
- Why did adding dependencies
DeltaSpike
allow injecting my beans without being used@Path
on them? - Looking for more, I figured out what is
DeltaSpike
usingWeld
for internal injection, and since I already useGlassFish 4.0
, I know that itWeld
already exists, so why doesn't injection work by default in the classJob
and in the classServiceResource
without adding@Path
to my beans? In fact, why is the addition@Path
even suggested in the Java tutorial? - Are there any bad side effects that I don't see in my code because I think I am mixing several DI methods here without understanding how they work?
Update: . After more searching, I realize that it Jersey
does not use Weld
for dependency injection, instead it uses HK2
, another structure which also turns out to be part of GlassFish
, when I try to inject AccountService
without using @Path
it shows the following exception
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object to inject in SystemInjecteeImpl (requiredType = AccountService , parent = ServiceResource , qualifiers = {} ...
So this updates the questions to the following:
- How to do injections
HK2
? // Without using@Path
as pointed out in the Java EE tutorial - If I was able to do
DI
with usingHK2
, can I use QuartzDeltaSpike
to executeDI
for the job? Can I mix two CDI fluids together to scan classes and inject?
I have put my source code on pastebin; pom.xml
here and Java
here
source to share
You don't need to provide an annotation Path
on the AccountService
CDI bean. If CDI is enabled in your application (either with empty beans.xml in CDI 1.0 or in discovery mode = everything in CDI> 1.0), you can have @Inject
any CDI bean in your JAX-RS resource. Therefore, you just need to write the following class:
@Path("/")
public class ServiceResource {
@Inject
private AccountService accountService;
@GET
@Path("/account/get")
public Account getAccount(@QueryParam("id") String id) {
return accountService.get(id);
}
}
@javax.inject.Singleton
public class AccountService {
public void Account get(String id){...}
}
The article you linked in your post is about mixing EJB and CDI annotations. For example, you can combine annotations @Stateless
and @Path
. This is interesting, for example, because you can:
- Benefits of EJB transaction on Rest resource (even though you can use hook bindings
@Transactional
) - Install resource pool
- and etc.
Note that this all works without the help of the deltaspike dependency.
For your second question, since Quartz manages its threads, the classes are not handled by CDI, so you cannot inject beans into the quartz classes. The purpose of the deltaspike module is to allow CDI beans to be injected into Quartz Jobs. Internally, deltaspike manages CDI contexts.
EDIT
For your latest questions:
-
The problem with HK2 comes from missing dependencies (in the app or server). As mentioned in a previous comment, I was able to deploy your application on Glassfish 4 (build 89) with the source files you provided.
-
As far as CDI integration with quartz goes, I think it's best to implement your own
JobFactory
and speed things up withBeanManager
. Take a look at this link: https://devsoap.com/injecting-cdi-managed-beans-into-quarz-jobs/
source to share
First of all, the nested resources (beans) and Jersey endpoint class (injection point) must be CDI-Aware. It must be recognized by CDI. We can use bean -discovery-mode = "all" - then CDI scan ALL classes or bean -discovery-mode = "annotated" and MARK our class with PROPER annotation: hence: Bean definition of annotations . I prefer @Dependent or @RequestScoped
Then we have to use Jersey Extension
<dependency>
<groupId>org.glassfish.jersey.ext.cdi</groupId>
<artifactId>jersey-cdi1x-servlet</artifactId>
<version>{version}</version>
<scope>runtime</scope>
</dependency>
`
for CDI connection with HK2 detection engine. Here is the Official Oracle Guide
source to share
The default bean.xml lookup mode (in Java EE 7) is "annotated". This means that only beans that have CDI annotations are recognized and managed by CDI.
Your AccountJob class is not annotated. If you want CDI to be able to inject a service into it, you need to annotate it with a scope annotation, for example. @ApplicationScoped.
Another option is to create a CDI producer to create AccountJob beans. See: http://docs.jboss.org/weld/reference/latest/en-US/html_single/#_producer_methods
source to share