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.
Related
My app is being deployed on to IBM WebSphere. I have a simple service and I'd like to know how dependency injection works in this case.
// stateless EJB
#Stateless
public class UserService {
private UserDAO userDAO;
// btw, UserDAO is stateless EJB as well
#Inject
public UserService(UserDAO userDAO) {
this.userDAO = userDAO;
}
// biz methods ...
}
It fails with the following error:
[ERROR ] CWWKZ0002E: An exception occurred while starting the
application my-app. The exception message was:
com.ibm.ws.container.service.state.StateChangeException:
com.ibm.ws.cdi.CDIException:
com.ibm.wsspi.injectionengine.InjectionException:
com.ibm.ejs.container.EJBConfigurationException: EJB class
com.demo.app.UserService must have a
public constructor that takes no parameters
I remember there was something in EJB spec that says: the class must have a public constructor that takes no parameters and it makes sense for me that the bean instance is first instantiated by the container and afterward dependency injection is done.
On the other hand, I've found this in WELD docs:
First, the container calls the bean constructor (the default
constructor or the one annotated #Inject), to obtain an instance of
the bean.
And I am confused a little bit, why my EJB cannot be instantiated.
How is the EJB instance being created and dependencies injected when we have constructor injection point?
Any ideas? :)
So what happens is that you do not meet the requirements for initializing EJB beans.
CDI spec has some limitations on constructors - either no-args or one with #Inject.
But there is also this chapter, which specifies that in EE, the set of rules is extended by what EJB session beans require.
And now we are getting into EJB spec which requires a no-arg constructor on a bean.
This should be in chapter Enterprise Bean Class where it states
The class must define a public constructor that takes no arguments.
Now, finally moving on to whether this should work - e.g. can you have an EJB bean using CDI constructor injection?
Well, let's have a look at CDI TCK, a set of tests that all implementation and containers have to pass in order to be able to claim they implement CDI.
There, we can see this bean and this test using it - so yea, this can work, but you need to have both constructors.
EJBs are registered as CDI beans. But first they have to meet the requirements of the EJB spec.
I guess it works just by providing the no-args constructor.
the creation of EJB session beans is done by EJB container but it can choose to use CDI to provide EE resource injection but EJB resolution is delegated to the container
https://docs.jboss.org/weld/reference/2.1.0.Final/en-US/html/ri-spi.html says:
Alternatively, the integrator may choose to use CDI to provide EE
resource injection. In this case, the EE_INJECT environment should be
used, and the integrator should implement the Section A.1.4, “EJB
services”, Section A.1.7, “Resource Services” and Section A.1.5, “JPA
services”.
....
Weld registers resource injection points with
EjbInjectionServices, JpaInjectionServices, ResourceInjectionServices
and JaxwsInjectionServices implementations upfront (at bootstrap).
This allows validation of resource injection points to be performed at
boot time rather than runtime
if you interested in how CDI and EJB are integrated. you can have a look at the code of weld-EJB module and weld-integration(glassfish code)
I have two application deployed on glassfish - application A and B.
Both are deployed as war package, application B uses some components from application A.
Now in application A I have an interface:
public interface BusinessInterface() extends SomeOtherInterface {
void someAction();
}
I have 3 implementations of this interface - two in application A, one in application B: BusinessInterfaceA1, BusinessInterfaceA2, BusinessInterfaceB
As long as all of them are CDIBeans, everything is fine - I'm using custom #Qualifier annotations (#BusinessInterfaceA1, #BusinessInterfaceA2) and #Default annotation for B's implementation to distinguish them.
But now I need both application's A implementations to be Stateful EJBs and this is where it becomes funny.
When I just add #Statefull annotation on both implementations, a I got something like this:
javax.servlet.ServletException: org.jboss.weld.exceptions.WeldException: WELD-000049
details:
java.lang.IllegalStateException: Unable to convert ejbRef for ejb BusinessInterfaceA1 to a business object of type interface SomeOtherInterface
How can I fix it? I need all implementations to be avaliable in a way I could inject them like
#Inject #SomeAnnotation private BusinessInterface businessInterface;
It is bug in Glassfish 3.1. The workaround is, to mark implementation with all required interfaces, e.g.:
#Statefull/#Stateless
public class BusinessInterfaceImpl implements BusinessInterface, SomeOtherInterface {
// implementation
}
Even BusinessInterface extends SomeOtherInterface, and from Java specs its useless to do that, but as a workaround for that bug it works.
Another solution is to use Glassfish 4.0
Such simple question but I can't find the answer in google. I have two osgi bundles on glassfish4:
Bundle1 - Servlet
Bundle2 - MyEJB1, MyEJB2. Both EJB are local, stateless, implements the same interface (MyLocalInterface) and every has its own
name.
In servlet I inject EJB like this:
#Inject #OSGiService (dynamic = true)
private MyLocalInterface ejb;
How can I distinguish via names these two EJB for injection? As I think I should use serviceCriteria in #OSGiService but how?
EDIT:
I have a lot of osgi services (not ejb, simple osgi services) with the same interface. To distinguish them I use
#Inject #OSGiService(dynamic=true, serviceCriteria = "(component.name=com.bla.bla)").
The same I need for ejb.
EDIT2:
Up to now the only way I've found is to use jndi-name as both cdi beans are EJB:
#Inject #OSGiService(dynamic=true, serviceCriteria = "(jndi-name=java:global/....)").
Be careful! It won't work without "()"!
But this solution could be much better if we solve this question: Glassfish 4: how to set application or module name for JNDI
Simply use the #Named("myname") annotation. It is already defined in the standard.
Im writing JavaEE application, which contains of three modules.
In web module I need to inject ejb bean (in Stripes action bean method), which is located in ejb module.
So i followed this tutorial
http://www.stripesframework.org/display/stripes/Stripes+Injection+Enricher
My code goes like this>
#EJB
private CustomerServiceLocal customerService; //service layer for customer
and after deploying on GlassFish it returns following exception>
No EJB found in JNDI, tried the following names:
list of JNDI that it tried.
The bean is declared as follows>
#Stateless
#Local(value=CustomerServiceLocal.class)
#LocalBean
public class CustomerService implements CustomerServiceLocal {
interface>
#Local
public interface CustomerServiceLocal
I dont know what code could be relevant, so if I missed something just tell me what should I copied here.
So do you have any idea how to solve it ? How to set EJB Bean in different module in Stripes ActionBean method ?
It seems that stripes injection enricher is somehow wrongly configurated.
The problem is I need that service layer make work, since it is responsible for work with DB, and without it, I can just work with temporal local objects, which is useless.
Best regards,
OSiRiS
I just want to confirm that I fully understood the prerequisites for CDI to work. If I have a class A:
public class A {
#Inject private B b;
}
Now when I instantiate this class using:
A a = new A();
In that case, A.b will be null.
But if I define in another class a member:
#Inject A a;
and later use a, a.b will be correctly populated?
Does CDI only work if the class requiring an injection was also created by CDI container? Or what am I missing if injections turn out to be null while creating a POJO using ordinary instantiation with new (yes, I got beans.xml in place)?
Does CDI only work if the class requiring an injection was also
created by CDI container?
Yes, that's pretty much it. The lifecycle of a ManagedBean is controlled by the container and should never be instantiated with the new keyword (BTW: the same is true for EJBs & Spring beans). If you need to create a new ManagedBean you will probably want to use a producer method.
While others have stated correctly that for the most part DI containers will not inject dependencies into bean that they did not instantiate this is not entirely true.
Spring has a wonderful feature that will autowire the bean when you create it using new A().
You just have to use AspectJ and mark your bean with the #Configurable annotation.
#Configurable
public class A {
#Inject private B b;
}
Its actually kind of an awesome feature cause you can do Active Record style POJO's while still honoring your DI (its in fact how Spring Roo does it).
You should also know that with Spring you can autowire a bean programmatically after its been instantiated with the AutowireCapableBeanFactory. This is how it typically autowires JUnit Test Case Classes because JUnit creates the test case classes.
Yes Spring is not CDI but in theory you could write your own #Configurable for CDI or there is probably a CDI way of doing the above.
That being said the above is sort of a sophisticated feature (and kind of a hack) and as #JanGroth mentioned understaning the lifecycle bean management of the container is critical whether its CDI, Spring, Guice etc.
You may use BeanProvider.injectFields(myObject); from Apache DeltaSpike.
Yes, #Inject works only inside a container because it is done using interceptors on method calls. When the container creates a bean it wrappes it in interceptors which perform injection, and in the case of instantiation using new no interceptors will be called during bean method invocation, and there will be no injection.
Here's the conditions needed for a class to be a managed bean (and hence, for the #Inject annotation to work on its fields/methods):
http://docs.oracle.com/javaee/6/tutorial/doc/gjfzi.html