Injection in entity listener with Quarkus - jpa

I'm trying to inject a bean into an entity listener in a Quarkus-application:
#ApplicationScoped
public class MyEntityListener implements Serializable {
#Inject
MyService service;
#PrePersist
#PreUpdate
public void checkWrite(BaseEntity entity) {
service.check(entity);
}
}
But service is always null. Changing scope to #SessionScoped has no effect.
According to this 2 SO-discussions, this should be possible:
CDI injection in EntityListeners
How can I use an EJB in a EntityListener?
I couldn't find any information about which JPA-version Quarkus is using, but since it is a state-of-the-art-framework I think it is JPA 2.1?
So should this be possible and if yes, what am I doing wrong?

I found a Quarkus-issue addressing this problem: https://github.com/quarkusio/quarkus/issues/6948
Seems like Quarkus is lacking support for this feature and maybe it will be implemented in the future. There's also a workaround described.

Related

Injecting a Stateless EJB into another Stateless and using PersistenceContext

With JEE 5 / EJB 3.0 life of Java developers became much more easier. Later, influenced by Spring and CDI, similar approaches were also adopted in JEE.
Now, I hope I am doing it right, but just to be sure:
I have several Stateless EJBs, which all query and / or modify the database. An example is
#Stateless
public class AddressDBService {
#PersistenceContext
protected EntityManager em;
Some of the Stateless EJB refer the other services like this:
#Stateless
public class AVeDBService {
#PersistenceContext
protected EntityManager em;
#Inject
private HomeToDealDBService homeToDealDBService;
#Inject
private AddressDBService addressDBservice;
and in the Stateless EJBs I have public methods like the ones below:
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void saveEntity(Home home) throws EntityExistsException {
this.em.persist(home);
addressDBservice.saveAddress(home.getMainAddress(), home);
}
While I am almost certain this usage is correct and thread-safe (the above services are in turn injected into JSF Managed Beans).
Could somebody confirm that my usage is correct, thread-safe and conforms good practices?
My usage seems to be conform with the following questions:
Is EntityManager really thread-safe?
Stateless EJB with more injected EJBs instances
The "is correct?" question can't be answered without know the goal of the project.
It could works? Yes, you have posted java-ee code that could deploy, but is not enough.
I usually use BCE (Boundary Control Entity) pattern and Domain Driven pattern.
In this pattern we use EJB for business logic services or endpoint (JAX-RS) and all other injections, that are the Control part, are CDI objects.
Entities (JPA) could use cascade to avoid to manually save related entities:
addressDBservice.saveAddress(home.getMainAddress(), home);
can be avoided if you define the entity like this:
#Entity
public class Home {
#ManyToOne(cascade=ALL)
private Address mainAddress;
}
The #TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) annotation usually respond to a specific transactions behavior, is not required, so is correct only if is what you want to do.

How to make #EJB injection work on the server?

Looking at this answer, it says:
If you don't want to use an Application Client Container and instead just run the application client class through a java command, injection won't be possible and you'll have to perform a JNDI lookup.
However, given that I am trying to inject a DAO bean like the example shown here, if I cannot do the automatic injecting, it means my application must manually do the JNDI lookup and all the transaction begin/end that I would get for free if the #EJB actually worked.
However, since everything is all within the same Eclipse EJB Project (it also failed with the same null handle when I had my client code in a Dynamic Web Project), surely there must be an easy way to get it all working? Can anyone suggest what I am doing wrong?
Finally, this article suggests that DAOs are not needed, but if I replace within my EJB:
#EJB MyDao dao;
with the more direct:
#PersistenceContext private EntityManager em;
I still get the similar null value; is this the same injection failure problem?
NB: I have just noticed this answer:
This is a bug in Glassfish (apparently in the web services stack).
I am running v4.0 Build 89, which still has this bug? Does this mean I have to do all JPA actions the long-winded way?
I eventually found out that the problem/issue is that in order to use injection of the #PersistenceContext the class MUST be a bean itself. This is hinted at in the example on Wikipedia:
#Stateless
public class CustomerService {
#PersistenceContext
private EntityManager entityManager;
public void addCustomer(Customer customer) {
entityManager.persist(customer);
}
}
I could delete this question, but perhaps leaving this answer might provide a hint to someone, or at least show them a minimal working example of EJB and JPA.

Custom Annotation vs using #Named in JEE6

Is there any difference between these two alternatives ... can they both be used interchangeably?
(A) Creating a custom annotation so #Inject can be used instead of #PersistenceContext within a DAO, as shown in the answer to - how-to-stack-custom-annotation-in-java-with-inject-annotation
(B) Using #Named("yourName") to qualify the Producer, such as the following code sample.
public class Resources {
/**
* EntityManager's persistence context is defined here so the #Inject annotation may be used in referencing classes.
*/
#Produces
#Named("MyEm")
#PersistenceContext(unitName = "jboss.managed")
private EntityManager em;
}
#Stateless
public class FiletracksentHome {
..
#Inject
#Named("MyEm")
private EntityManager entityManager;
..
}
They are interchangable, but you should use (A).
The #Named annotation is primarily used for being able to access the object via expression language (EL), e.g. in a JSF view.
The problem is, that the resolution is done via String, thus being neither type safe, nor usually being automatically covered by refactorings in the IDE.
The CDI specification states that it should not be used for qualifying injection points if not be used to integrate legacy code.
Here's a nice article about this topic.

Injecting Guice into JAX-WS POJO

I am using JBoss 7.1 and Java 1.6.
I would like to integrate a Guice service with by JAX-WS endpoint. Using the interceptor pattern described by Gunnar.Morling I am able to properly instantiate the Guice modules when using a stateless bean as a webservice. However i am not able to do the same with a simple POJO annotated webservice. Is this possible has anyone found a workaround. Below is a summary of my efforts so far.
#UsesGuice #Interceptor
public class GuiceInterceptor {
#Inject
private GuiceInjectorHolderBean injectorHolder;
#AroundInvoke
public Object aroundAdvice(final InvocationContext ctx) throws Exception {
if (ctx.getTarget().getClass().isAnnotationPresent(UsesGuice.class)) {
final Injector injector = injectorHolder.getInjector();
injector.injectMembers(ctx.getTarget());
}
return ctx.proceed();
}
}
The GuiceInjectorHolderBean is the a sinlgeton bean responsible for triggering the guice wiring. The annotation class required follows
#Retention(RUNTIME)
#Target(TYPE)
#InterceptorBinding
public #interface UsesGuice {}
the JAX-WS POJO class
#UsesGuice
#WebService(serviceName = "EchoServiceService", portName = "EchoServicePort", ame = "EchoServiceImpl", targetNamespace = "lala")
public class EchoServiceImpl implements EchoService
{
#Inject
MyGuiceInjection injection;
#Override
#WebMethod
public String sayHello(final String msg)
{
return "Hello " + injection.call(msg);
}
}
Thanks in advance
Dimitri
Your Current Approach
In your code, javax.interceptor annotations #Interceptor, #InterceptorBinding and #AroundInvoke are supported by CDI and EJB standards and not by Guice. Guice uses proprietary AOP interception via org.aopalliance.intercept.MethodInterceptor interface and calling the method AbstractModule.bindInterceptor.
So you're trying to bootstrap Guice injection on your endpoint by:
using a non-Guice interceptor on the endpoint's method
within the #AroundInvoke method, programmatically invoking the Guice Injector, with injection target being the intercepted endpoint
That begs the Q, what to use for 1?
'Bootrap' Interception Mechanism for Your Current Approach
Obviously, an EJB interceptor works, as you've stated.
Other than an EJB or Guice AOP interceptor... an obvious alternative would be the standard, a CDI interceptor.
But that would make it all rather circular and heavy-weight... Why use CDI just to boostrap, so that you can configure and execute to your desired DI competitor: Guice?
Suggested Alternative Solution - JAX-WS Support for Manual Endpoint Instance Initialisation
If you want POJO web services, maybe consider back-tracking a bit, instead of interceptor-driven Guice initialisation, maybe this could be what you need:
javax.xml.ws.Endpoint.publish(String address, Object implementor)
Endpoint.publish javadoc
Initialise Guice in the standard way, use injector.getInstance() to construct your endpoint instance, and then use Endpoint.publish to set the endpoint instance against the port. The following gives a good example:
Using Guice 3 with JAX-WS in Java 6 outside web container

Using CDI + WS/RS + JPA to build an app

#Path(value = "/user")
#Stateless
public class UserService {
#Inject
private UserManager manager;
#Path(value = "/create")
#GET
#Produces(value = MediaType.TEXT_PLAIN)
public String doCreate(#QueryParam(value = "name") String name) {
manager.createUser(name);
return "OK";
}
}
here is the user manager impl
public class UserManager {
#PersistenceContext(unitName = "shop")
private EntityManager em;
public void createUser(String name) {
User user = new User();
user.setName(name);
// skip some more initializations
em.persist(user);
}
}
the problem is if i do not mark UserService as #Stateless then the manager field is null
but if i mark #Stateless, i can have the manager field injected, and the application works as i can get the data saved into db
just wondering, what is the reason behind this?
and is this the preferred way to wiring the application?
well, i am thinking to pull out the EntityManager to a producer, so that it can be shared
the problem is if I do not mark UserService as #Stateless then the manager field is null
For injection to occur, the class has to be a managed component such as Enterprise Beans, Servlets, Filters, JSF managed beans, etc or CDI managed bean (this is the new part with Java EE 6, you can make any class a managed bean with CDI).
So, if you don't make your JAX-RS endpoint an EJB, how to enable injection? This is nicely explained in JAX-RS and CDI integration using Glassfish v3:
There are two ways CDI managed beans
are enabled:
instantiated by CDI, life-cycle managed by Jersey. Annotate with
#ManagedBean and optionally annotate
with a Jersey scope annotation.
instantiated and managed by CDI. Annotate with a CDI scope annotation,
like #RequestScoped (no #ManagedBean
is required)
I also suggest checking the resources below.
and is this the preferred way to wiring the application?
I'd say yes. CDI is very nice and... don't you like injection?
well, I am thinking to pull out the EntityManager to a producer, so that it can be shared
Shared between what? And why? In you case, you should use an EntityManager with a lifetime that is scoped to a single transaction (a transaction-scoped persistence context). In other words, don't share it (and don't worry about opening and closing it for each request, this is not an expensive operation).
References
JPA 2.0 Specification
Section 7.6 "Container-managed Persistence Contexts"
Section 7.6.1 "Container-managed Transaction-scoped Persistence Context"
Section 7.6.2 "Container-managed Extended Persistence Context"
Resources
Dependency Injection in Java EE 6 - Part 1
Introducing the Java EE 6 Platform: Part 1
TOTD #124: Using CDI + JPA with JAX-RS and JAX-WS
The #Singleton annotation will help: http://www.mentby.com/paul-sandoz/jax-rs-on-glassfish-31-ejb-injection.html