A JAX-RS root resource is defined by a #Path annotation and might use managed components for doing the actual work, for example:
#Path("resource")
public class Resource
{
#Inject
Worker worker;
#GET
public String getDetails() {
return worker.getDetails();
}
}
Now I can transfer this JAX-RS root resource to either a CDI bean:
#RequestScoped
#Path("resource")
public class Resource {...}
Or to an EJB:
#Stateless
#Path("resource")
public class Resource {...}
So - what are the consequences of doing it the POJO, the CDI or the EJB way? From the outside, a request to the URL delivers three times the same thing, but what happens under the hood and how do the injected components relate to each case?
It pretty much comes down to context. Do you need the extra facilities that an EJB provides (well defined transaction semantics, proxied stateless pooled handlers, cluster support, etc.), or do you just need dependency injection?
Just using a CDI bean will give you that off the shelf, if that's all you need. If you don't even need that, a POJO will provide your simplest bang for the buck.
Related
I have several service like that:
#Singleton
public SimpleService {
...
}
I have Managed Bean #ViewScoped which should create some complex objects. These objects should execute business-logic. I need to pass these services to this object.
Example of Managed Bean:
#ManagedBean
#ViewScoped
public class ExampleBean {
#Inject
private SimpleService simpleService;
...
public void customLogic() {
// in this method I should create complex object which should have services and some data.
// current implementation
ComplexObject object = new ComplexObject(SimpleService simpleService, ...)
}
}
Services are injected to Managed Bean by #Inject annotation. For creating these objects - I'm using the constructor and pass these services as params. The question is: can I have better solution than passing services in constructor?
You can:
Inject by method:
private MyService myService;
#Inject
public void setMyService(MyService ms) {
this.myService = ms;
}
Inject by field:
#Inject
private MyService myService;
Fetch reference through CDI (not recommended, except in advanced usecases):
...
MyService myService = CDI.current().select(MyService.class).get();
...
Fetch reference through BeanManager (not recommended, except in advanced usecases or CDI 1.0):
...
BeanManager beanManager = CDI.getBeanManager(); // you can fetch a BeanManager reference by several other methods, I use CDI for simplicity here
MyService myService = beanManager.getReference(MyService.class);
...
If your #Singleton annotation is javax.ejb.Singleton and not javax.inject.Singleton, then your bean is actually a EJB and you can also use any mechanism that allows you to access EJB, such as #Resource annotations, or through the JNDI context.
Personally I tend to inject by method as I find it the most flexible option most of the time. In my opinion it is also the most "portable" to other frameworks (e.g: Spring)
Remember that when you use either the CDI.current() or the BeanManager methods to fetch #Dependent beans, you are responsible to manually destroy the fetched bean when you are done with it, so that you do not fall into this CDI-related memory leak. When using CDI.current() it is as easy as saving the Instance reference and invoking it afterwards:
...
Instance<MyService> msInstance = CDI.current().select(MyService.class);
MyService myService = msInstance.get();
...
msInstance.destroy(myService);
...
The BeanManager method is too low-level and should only be used in CDI 1.0 environments (back when the CDI class did not exist yet). You can read the linked StackOverflow question for more details.
What you are doing is perfectly fine. You are using the ManagedBean as a bridge to inject the services and then passing the injected variables to a ComplexObject that need the services.
The only restriction that should be considered is, could the ComplexObject class be a ManagedBean itself? That way you could inject everything directly on it, but if it is not possible, you may use the bean for that.
I prefer the inject by field option mentioned because I think it is a little more readable.
I'm using CDI in a Jersey app. On root resources, CDI injection works as expected, but whenever I return a sub-resource, the CDI injection sources are not available.
My root resource with sub-resource locator:
#Path("")
public class MyResource {
#Inject #Named("name") // works
private String name;
#Context
private ResourceContext context;
#Path("test2")
public Object test2() {
return MySubResource.class;
//return context.getResource(MySubResource.class); // this does not work either
}
}
The sub-resource:
public class MySubResource {
#Inject #Named("name") // error
private String name;
#GET
public Response test() {
return Response.ok("Name in sub resource: " + name).build();
}
}
Error:
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=String,parent=MySubResource,qualifiers={#javax.inject.Named(value=name)},position=-1,optional=false,self=false,unqualified=null,1235803160)
I'm using org.glassfish.jersey.ext.cdi:jersey-cdi1x and Weld dependencies, running on Undertow, with the Weld servlet listener added to the deployment.
Again, the same injection on the root resource does work. The #Named("name") String is produced by an #ApplicationScoped producer.
Is this not supposed to work? What am I missing?
Minimal example Maven project available here:
https://gitlab.com/Victor8321/jersey-sub-resource-cdi
Note: An open issue for this exists, but not sure what the official stance on this is: https://java.net/jira/browse/JERSEY-3184
As pointed out in https://github.com/eclipse-ee4j/jersey/issues/3456, adding a dummy #Path("xyz") to the sub-resource class is a "fix". However, that exposes your sub-resource under the dummy path.
Injecting an instance just through CDI works as well (#Inject Instance<MySubResource> ..), but then Jersey-managed resources aren't available for injection, such as #Context HttpServletRequest.
I've found 2 other approaches that fully work (both CDI injection and JAX-RS injection) and have no side effects (as with #Path):
Annotate the sub resource class with #Provider.
register() the sub resource class in the ResourceConfig (or Application).
Both approaches seem to work because they make Jersey - and in turn, CDI - aware of the class.
Note: I've updated my example project accordingly for future reference.
I am trying to use and understand CDI, when I use #Inject in a simple pojo class, it throws me NPE.
example
Greeting.java
public Class Greeting {
public String greet() {
System.out.println("Hello");
}
}
Test.java
import javax.inject.Inject;
public class Test {
#Inject
private Greeting greeting;
public void testGreet() {
greeting.testGreet();
}
}
When I call testGreet() it throws NPE, why is the greeting instance null. Does #Inject way of adding dependency only be used in container managed bean?
Note: jar is not the problem here.
TL;DR:
#Inject-annotated fields are only populated for container-instantiated beans.
Long version:
The CDI container provides you a lot of utilities for easily injecting dependencies to your beans, but it doesn't work by magic. The container can only populate the annotated fields of a client bean if the client bean itself was instantiated by the container. When the container is instantiating the object the sequence of events is as follows:
Your bean's constructor is called.
#Inject-annotated fields (and some other
annotations, #PersistenceContext and #EJB for instance) are
populated.
#PostConstruct-annotated no-args method is called.
Your bean is finished.
You're facing a classic bootstrapping problem, how to move from non-container-managed code into container-managed code. Your options are:
Get access to an instance of BeanManager from your JavaEE container via JNDI lookup. This is technical and a bit clumsy.
Use a CDI extension library such as Apache DeltaSpike. (Example: BeanProvider.getContextualReference(Test.class, false);)
Modify your application to start in a situation where you can inject your Test class rather than calling new Test();. This can be done for example by setting up a startup singleton ejb, which calls your test in it's #PostConstruct-annotated initialisation.
Hope this helps.
You need a JavaEE container, and than you need to define Greeting and Test as managed beans. After that you can inject one in another.
Try to take a look at:
https://docs.oracle.com/javaee/6/tutorial/doc/girch.html
Your class should be implemented from Serializable for being injectable as a "CDI Bean"
I have the following configuration:
1 EAR on one GF containing 2 EJB-JARs with EJB components.
1 WAR on another Glassfish server (=> other JVM) containing web components accessing the EJB components.
I have 2 EJB business services in each EJB-JAR of my EAR, and they are all developped like this:
#Remote
public interface ServiceAItf {
...
}
#Stateless
#Local
public class ServiceAImpl implements ServiceAItf {
...
}
In my WAR, I access the EJB components via an explicit "InitialContext.lookup" on the remote interface.
In my EAR, I am quite confused about the best practice for injection, in terms of performance, architecture, and so on...
I have the following questions:
As you can see, I have declared the annotation "#Local" on the service implementation without defining the local interface. Is it correct? At least I have no error at deployment. But maybe I should use the "#LocalBean" annotation instead? I suppose that the "#LocalBean" annotation simply allows to invoke the implementation directly as a "Local" EJB, but you have to use the implementation in your code like this:
#Stateless
#Local
public class ServiceBImpl implements ServiceBItf {
#EJB
private ServiceAImpl serviceA;
...
}
What is the best way to inject one EJB into another one?
It works like this:
#Stateless
#Local
public class ServiceBImpl implements ServiceBItf {
#EJB
private ServiceAItf serviceA;
...
}
But from what I noticed, the "serviceA" injected is the remote proxy, while it is in the same JVM within the same EAR file. So I suppose that there will be an impact on the performance. That's why I have tried to inject the service like this:
#Stateless
#Local
public class ServiceBImpl implements ServiceBItf {
#Inject
private ServiceAItf serviceA;
...
}
But it doesn't work in GF, I have the following exception:
WELD-001408 Unsatisfied dependencies for type [...] ...
I then tried to create a local interface, and the injection via the annotation "#Inject" works when both services
Even if I create a local interface like this, the service is not injected via the annotation "#Inject" but is null:
#Local
public interface ServiceALocalItf {
...
}
I read a lot of articles where it is highly recommended to use "#Inject" instead of "#EJB" when it is for a local invocation. That leads me to the following question: in which case the "#Local" EJB invocation is recommended (or simply used)?
After all this analysis, I come to the following conclusion:
For each service, I create a "#Local" and a "#Remote" interface.
From WAR to EJB-JAR of EAR, make a JNDI lookup to the remote interface.
From EJB-JAR to EJB-JAR, make an injection via "#EJB" to the local interface.
For two services within the same EJB-JAR, make an injection via "#Inject" to the local interface.
What do you think about it? Is it correct?
As you can see, I have declared the annotation "#Local" on the service
implementation without defining the local interface. Is it correct?
With EJB 3.1, the requirement for local interfaces was dropped. No need to write them unless you explicitly need them.
What is the best way to inject one EJB into another one?
Couple of things to write here:
With Java EE 6, Java Enterprise has changed. A new JSR defines a so-called managed bean (don't confuse with JSF managed beans) as a sort of minimum component that can still benefit from the container in terms of dependency injection and lifecycle management. This means: If you have a component and "just" want to use DI and let the container control its lifecycle, you do not need to use EJBs for it. You'll end up using EJBs if - and only if - you explicitly need EJB functionality like transaction handling, pooling, passivation and clustering.
This makes the answer to your question come in three parts:
Use #Inject over #EJB, the concept of CDI (a) works for all managed
beans (this includes EJBs) and (b) is stateful and therefore far
superior over pure #EJB DI
Are you sure that you need EJBs for your
components?
It's definitely worthwhile to have a look at the CDI
documentation
#Inject annotation is used for java beans(POJOs) while #EJB annotation is used for enterprise java beans. When a container inject an ejb provided by #EJB annotation to another bean it also controls that ejb's lifecycle, performs pooling for stateless beans and so on(when bean which is going to be injected is not deployed the reference will be null). If you use #Inject annotation CDI mechanism simply find and create an instance of injected resource like with new operator(if the implementation of interface which is going to be injected doesn't exist the reference will be null). You can use qualifiers with #Inject annotation to choose different implementation of injected interface.
Being completely new to Java EE (but not to Java itself) I'm trying to build a very simple "Enterprise Application" with Hibernate as JPA provider and JSF as the actual UI framework. For this purposes I'm using the NetBeans 7 with GlassFish 3.1.
{ApplicationName}-ejb:
I've accomplished to generate entity classes from database and local sesssion beans for these entities. Beans.xml is in place.
#Stateless
public class QuestFacade extends AbstractFacade<Quest> implements QuestFacadeLocal {
// some methods here as well as EntityManager injection ...
}
{ApplicationName}-war:
I've created a simple POJO as a backing bean for the JSF page. I've annotated it with javax.inject.#Named and javax.enterprise.context.#SessionScoped. This backing bean is now accessible from the JSF page as well as being injected when the actual page is accessed. Beans.xml is in place as well.
#Named
#SessionScoped
public class QuestBean implements Serializable {
#EJB
protected QuestFacade questFacade;
// several methods delegating lookups to the questFacade ...
}
Having this deployed and page accessed, I'm, however, getting an error from GlassFish that the QuestFacade cannot be looked up by the JNDI.
The stacktrace is quite long but the initial cause could be enough:
Caused by: javax.naming.NamingException: Lookup failed for 'model.session.QuestFacade#model.session.QuestFacade' in SerialContext[myEnv={java.naming.factory.initial=com.sun.enterprise.naming.impl.SerialInitContextFactory, java.naming.factory.state=com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl, java.naming.factory.url.pkgs=com.sun.enterprise.naming} [Root exception is javax.naming.NameNotFoundException: model.session.QuestFacade#model.session.QuestFacade not found]
at com.sun.enterprise.naming.impl.SerialContext.lookup(SerialContext.java:518)
at com.sun.enterprise.naming.impl.SerialContext.lookup(SerialContext.java:455)
at javax.naming.InitialContext.lookup(InitialContext.java:392)
at javax.naming.InitialContext.lookup(InitialContext.java:392)
at com.sun.ejb.EjbNamingReferenceManagerImpl.resolveEjbReference(EjbNamingReferenceManagerImpl.java:173)
... 74 more
Caused by: javax.naming.NameNotFoundException: model.session.QuestFacade#model.session.QuestFacade not found
at com.sun.enterprise.naming.impl.TransientContext.doLookup(TransientContext.java:248)
at com.sun.enterprise.naming.impl.TransientContext.lookup(TransientContext.java:215)
at com.sun.enterprise.naming.impl.SerialContextProviderImpl.lookup(SerialContextProviderImpl.java:77)
at com.sun.enterprise.naming.impl.LocalSerialContextProviderImpl.lookup(LocalSerialContextProviderImpl.java:119)
at com.sun.enterprise.naming.impl.SerialContext.lookup(SerialContext.java:505)
... 78 more
I understand that I'm persuading GlassFish to inject an EJB from a different module within the same application. Should the #Remote interface be used instead? I've also tried to explicitely specify the name for both #Stateless and #EJB annotation but without any success.
I believe that I'm doing something fundamentaly wrong, but I cannot find out what.
Any suggestion or would be greatly appreciated!
I believe that I'm doing something fundamentaly wrong, but I cannot find out what.
What you're doing wrong is that if you implement a business interface (either #Local or #Remote), then you must declare the variable where injection takes place as having the type of that interface, not of the actual bean class.
So in your case:
#Named
#SessionScoped
public class QuestBean implements Serializable {
#EJB
protected QuestFacadeLocal questFacade;
// several methods delegating lookups to the questFacade ...
}
However, a business interface is not required in EJB when you're doing local (in-jvm) communication. As you discovered, if you don't specify a business interface at all for your EJB, you can inject the bean class itself. This is because you then automatically get the so-called no-interface view.
If you want, you can optionally declare that you want BOTH the local view and the no-interface view. In that way, you can inject your bean class in places whether either the bean type itself is declared or its business interface. For this you use the #LocalBean.
#Stateless
#LocalBean
public class QuestFacade extends AbstractFacade<Quest> implements QuestFacadeLocal {
// some methods here as well as EntityManager injection ...
}
Injection can thus happen in two ways now:
#Named
#SessionScoped
public class QuestBean implements Serializable {
#EJB
protected QuestFacadeLocal questFacade; // possible because of local view
#EJB
protected QuestFacade questFacadeN; // possible because of no-interface view
// several methods delegating lookups to the questFacade ...
}
In practice I didn't found much use for having both methods available at the same time though, but maybe this adds to your understanding.
Apparently the problem was that I generated #Local session beans. Per this tutorial it is no longer necessary (?) to specify the #Local or #Remote interface. I still not completely understand the problem though.
I Hope this answer could potentialy save up some time to somebody :-)
Jarda