Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Correct way to Integrate JAX-RS with CDI?

I used to integrate Service and DAO beans in Jersey REST resources by annotating them with @Path following Java EE tutorial

In general, for JAX-RS to work with enterprise beans, you need to annotate the class of a bean with @Path to convert it to a root resource class. You can use the @Path annotation with stateless session beans and singleton POJO beans.

So my code used to be 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 a Quartz Job into my application, and I wanted to find a way to inject my AccountService inside a job 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 tells to use DeltaSpike to do the Job, so I added the following dependencies to my pom.xml, and without adding any more lines of code to any class the inejection of accountService to my 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 the @Path("") from AccountService, its instance is still injected fine inside ServiceResource, so my questions are the following:

  1. Why adding DeltaSpike dependencies made it possible to inject my beans without using @Path on them?
  2. By searching more, I understood that DeltaSpike internally uses Weld to do the injection, and since I am already using GlassFish 4.0, I know that Weld is already there, so why the injection is not working by default in my Job class and in ServiceResource class without adding @Path on my beans? Actually why adding @Path is even suggested in the Java tutorial?
  3. Is there any bad side effects that I don't see in my code, because I think that I am mixing multiple DI methods here without really understanding how do they work?

Update: After more search, I realize that Jersey doesn't use Weld for dependency injection, instead it uses HK2, a different framework that also happens to be a 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 available for injection at SystemInjecteeImpl(requiredType=AccountService,parent=ServiceResource,qualifiers={}...

So this updates the questions to the following:

  1. How to make HK2 injections works? // Without using @Path as mentioned in the Java EE Tutorial
  2. If I managed to to do DI with HK2, will it be safe to use DeltaSpike to do DI for the Quartz Job? Is it okay to mix two CDI framewroks together to scan the classes and do the injection?

I put my my source code on pastebin; pom.xml is here and the Java is here

like image 832
fujy Avatar asked May 17 '17 15:05

fujy


People also ask

What are the JAX-RS implementations?

While JAX-RS provides a faster way of developing web applications than servlets, the primary goal of JAX-RS is to build RESTful services. A server-side component API is defined in jaxrs-1.1 and jaxrs-2.0 to build REST applications. IBM® JAX-RS provides an implementation of the JAX-RS (JSR 311) specification.

Is javax enterprise context dependent Liberty gets the provider instance from CDI?

Liberty gets provider instance from CDI if the CDI scope of provider is Dependent or ApplicationScoped. Instance does not include CDI injection if it is from JAXRS.

Which of these are implementation of JAX-RS API?

JAX-RS is a standard defined in Java Specification Request 311 (JSR-311) and Jersey / RESTEasy are implementations of it.


2 Answers

You do not need to set the Path annotation on your AccountService CDI bean. If CDI is enabled on your application (either with empty beans.xml in CDI 1.0 or discovery-mode=all in CDI > 1.0), you can @Inject any CDI bean in your JAX-RS resource. So you just have 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 deals with mixing EJB and CDI annotations. For example you can mix @Stateless and @Path annotations. It's interesting for example because you can :

  • Benefit of EJB transaction in your Rest resource (even if now you can use @Transactional interceptor binding)
  • Set a pool of resources
  • etc.

Note that all of this works without the help of deltaspike dependency.

For your second question, as Quartz manages its own threads, classes are not handled by CDI so you can not inject beans in Quartz classes. The aim of the deltaspike module is to allow injecting CDI beans in Quartz Jobs. Internally, deltaspike controls CDI Contexts.

EDIT

For your last questions:

  • Your HK2 problem comes pretty sure from a missing dependency (in your application or server). As said in a previous comment, I managed to deploy your App on Glassfish 4 (build 89) with the source files you provided.

  • Regarding the integration of CDI with Quartz, I think the best is to implement your own JobFactory and instanciate your jobs using BeanManager. Take a look at this link : https://devsoap.com/injecting-cdi-managed-beans-into-quarz-jobs/

like image 113
Rouliboy Avatar answered Oct 08 '22 06:10

Rouliboy


First of all injected resources(beans) and Jersey Endpoint class(point of injection) must be CDI-Aware. It must be detecteable 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: from here : Bean defining annotations. I prefer@Dependent or @RequestScoped

Then we must use Jersey Extension

<dependency>
  <groupId>org.glassfish.jersey.ext.cdi</groupId>
  <artifactId>jersey-cdi1x-servlet</artifactId>
  <version>{version}</version>
  <scope>runtime</scope>
</dependency>

`

to connect CDI with HK2 discovery mechanism. Here is Official oracle Guideline

like image 36
vigor Avatar answered Oct 08 '22 06:10

vigor