Multiple instances of stateful EJB when injecting via CDI - dependency-injection

This is mostly about understanding the differences when injecting a stateful EJB (SFSB) with #Inject compared to injecting it with #EJB.
One of the main differences ought to be the contextual awareness when injecting via #Inject. So my assumption was that if I create two #RequestScoped beans and inject in each an SFSB twice (once with #Inject, once with #EJB), the SFSB injected via #Inject would be the same instance in both #RequestScoped beans, whereas the ones injected via #EJB would be different instances.
This assumption seems to be wrong, but I do not understand why. Shouldn't CDI be aware of the fact that both beans are #RequestScoped and therefore inject the same SFSB? Why is this not so, or is my test code somewhat flawed?
This is my SFSB and its interface:
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.Local;
import javax.ejb.PrePassivate;
import javax.ejb.Stateful;
import package.MyStateful;
#Stateful
#Local(MyStateful.class)
public class MyStatefulImpl implements MyStateful {
#PostConstruct
private void postConstruct() {
System.out.println("SFSB postconstruct ref: " + this.toString());
}
#PreDestroy
private void preDestroy() {
System.out.println("SFSB predestroy ref: " + this.toString());
}
#PrePassivate
private void prePassivate() {
System.out.println("SFSB prepassivate ref: " + this.toString());
}
#Override
public String myToString() {
return toString();
}
}
public interface MyStateful {
String myToString();
}
And this is one #RequestScoped bean:
import javax.ejb.EJB;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named;
import package.MyStateful;
#Named
#RequestScoped
public class MyFirstRequestScoped {
#Inject
MyStateful myStatefulByCDI;
#EJB
MyStateful myStatefulByEJB;
public MyStateful getMyStatefulByCDI() {
System.out.println("first#myStatefulByCDI proxy ref: " + myStatefulByCDI.toString());
System.out.println("first#myStatefulByCDI stateful ref: " + myStatefulByCDI.myToString());
return myStatefulByCDI;
}
public MyStateful getMyStatefulByEJB() {
System.out.println("first#myStatefulByEJB proxy ref: " + myStatefulByEJB.toString());
System.out.println("first#myStatefulByEJB stateful ref: " + myStatefulByEJB.myToString());
return myStatefulByEJB;
}
}
There is another #RequestScoped bean named MySecondRequestScoped with an analoguous implementation.
When these are called from a JSF xhtml page via EL (nothing special, just a <h:outputText value="#{myFirstRequestScoped.myStatefulByCDI}" /> and so on to trigger their creation), this is the console output (WebSphere ApplicationServer 8.5.5.0):
[1/4/14 12:39:11:759 CET] 000000dc SystemOut O SFSB postconstruct ref: package.MyStatefulImpl#c03fcdee
[1/4/14 12:39:11:761 CET] 000000dc SystemOut O SFSB postconstruct ref: package.MyStatefulImpl#36b3bb10
[1/4/14 12:39:11:761 CET] 000000dc SystemOut O first#myStatefulByCDI proxy ref: package.EJSLocal0SFMyStatefulImpl_8d170245#48da7f98(BeanId(Project#ProjectEJB.jar#MyStatefulImpl, 5D0CBA11-0143-4000-E000-6A007F000001))
[1/4/14 12:39:11:762 CET] 000000dc SystemOut O first#myStatefulByCDI stateful ref: package.MyStatefulImpl#36b3bb10
[1/4/14 12:39:11:768 CET] 000000dc SystemOut O SFSB postconstruct ref: package.MyStatefulImpl#9b3971c7
[1/4/14 12:39:11:768 CET] 000000dc SystemOut O SFSB postconstruct ref: package.MyStatefulImpl#456cec27
[1/4/14 12:39:11:769 CET] 000000dc SystemOut O second#myStatefulByCDI proxy ref: package.EJSLocal0SFMyStatefulImpl_8d170245#48da7fa1(BeanId(Project#ProjectEJB.jar#MyStatefulImpl, 5D0CBA18-0143-4000-E001-6A007F000001))
[1/4/14 12:39:11:769 CET] 000000dc SystemOut O second#myStatefulByCDI stateful ref: package.MyStatefulImpl#456cec27
[1/4/14 12:39:11:769 CET] 000000dc SystemOut O first#myStatefulByEJB proxy ref: package.EJSLocal0SFMyStatefulImpl_8d170245#48da7f9b(BeanId(Project#ProjectEJB.jar#MyStatefulImpl, 5D0CBA0E-0143-4000-E000-6A007F000001))
[1/4/14 12:39:11:769 CET] 000000dc SystemOut O first#myStatefulByEJB stateful ref: package.MyStatefulImpl#c03fcdee
[1/4/14 12:39:11:769 CET] 000000dc SystemOut O second#myStatefulByEJB proxy ref: package.EJSLocal0SFMyStatefulImpl_8d170245#48da7fa1(BeanId(Project#ProjectEJB.jar#MyStatefulImpl, 5D0CBA18-0143-4000-E000-6A007F000001))
[1/4/14 12:39:11:770 CET] 000000dc SystemOut O second#myStatefulByEJB stateful ref: package.MyStatefulImpl#9b3971c7
[1/4/14 12:39:11:848 CET] 000000dc SystemOut O SFSB predestroy ref: package.MyStatefulImpl#36b3bb10
[1/4/14 12:39:11:849 CET] 000000dc SystemOut O SFSB predestroy ref: package.MyStatefulImpl#456cec27
[1/4/14 12:50:11:765 CET] 00000120 SystemOut O SFSB prepassivate ref: package.MyStatefulImpl#c03fcdee
[1/4/14 12:50:11:766 CET] 00000120 SystemOut O SFSB prepassivate ref: package.MyStatefulImpl#9b3971c7
So it seems that:
4 instances of the SFSB are created; I would have expected this to be just 3. Those injected via #EJB aren't aware of the context, so I would have thought it's ok if they're created for every injection point. But since CDI should be aware of the context (#RequestScoped), I thought CDI would reinject the SFSB already created.
The only difference between #Inject and #EJB seems to be here that the life-cycle is automatically managed when injected via CDI - the method annotated #PreDestroy is called for those (36b3bb10 and 456cec27). These injected via #EJB (c03fcdee and 9b3971c7) are later only passivated and don't seem to be destroyed any time later.
The latter seems to be a good reason to use #Inject instead of #EJB, but what I don't understand is what is really meant by the contextual awareness of CDI, when there's a new instance of the SFSB created regardless of the scope?
By the way, this behaves the same when using #SessionScoped beans, even if the second bean is created after following a link to another page (to make sure that the SFSB injected via #Inject definitely exists already). Moreover, the SFSB instances injected via #EJB are created just once for the lifetime of the session, just like the ones injected via #Inject - so these seem to be aware of the context, too, somehow...? When mixing #SessionScoped and #RequestScoped beans, the #SessionScoped bean gets another instance of the SFSB injected than the #RequestScoped bean, which is fine - but which seems not to be a feature of CDI somehow, since this is true for both those instances of the SFSB injected via #Inject as well as for those injected via #EJB.
Edit: Conclusion of the observed behaviour:
The only difference between injecting an SFSB via #Inject and #EJB seems to be that in the former case the SFSB is automatically destroyed when the scope is left and in the latter case it's not. Is this correct? This would strike me as odd, since I expected CDI to behave differently...
Any hints about what I'm missing, i.e. misunderstanding when it comes to the "C" in "CDI"? I hope it's not some WebSphere "speciality"...

In order for your SFSB to be scoped to the request, you need to give it the #RequestScoped scope. Then you should see the same instance injected. Now since both of these are proxy'd, the easiest way to confirm is to set some value from one bean, and get the value from another bean.

Related

Get mapper from AuthenticationSuccessHandler

I'm trying to return some User data after a successful login in a Spring Boot application.
To do so, I need to serialize my Principal with the Jackson mapper that is already set-up at bootstrap via a factory.
Is there a way to inject it into the authentication handler?
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
response.setStatus(HttpServletResponse.SC_OK);
UserDetails me = (UserDetails) authentication.getPrincipal();
PrintWriter writer = response.getWriter();
/*mapper.writeValue(writer, user); <- how to get the mapper?
writer.flush();*/
}
tl;dr;
Instead of using a Factory, make the ObjectMapper a Spring bean. That way it can be injected into both Spring and Jersey components.
#Bean
public ObjectMapper mapper() {
ObjectMapper mapper = new ObjectMapper();
return mapper;
}
When you use an HK2 (Jersey's DI framework) Factory to create the ObjectMapper, the injection is only available to components that are retrieved through the HK2 ServiceLocator, which is the HK2 analogue of the Spring ApplicationContext.
When you use Spring-Boot with Jersey, how the two interact under the hood, is through the HK2 spring-bridge. So for example, say the AuthenticationSuccessHandler is registered in the ApplicationContext. With the spring-bridge configured, we can also get the AuthenticationSuccessHandler through the ServiceLocator
ServiceLocator l = ...
AuthenticationSuccessHandler handler = l.getService(AuthenticationSuccessHandler.class);
That should work. If you have the ObjectMapper binded through an HK2 Factory, and you were to #Inject the mapper into the AuthenticationSuccessHandler, it would be injected, when retrieved through the ServiceLocator.
That being said, Spring Security doesn't go through HK2, so it never has a chance to get the ObjectMapper through HK2. But if you register the mapper as a Spring bean, then Spring can inject it into the AuthenticationSuccessHandler. And the mapper is still available through HK2 ServiceLocator (because of the spring-bridge), so you can still use it with Jersey, same way as if it were binded with a Factory.

CDI Injection in a Wildfly application

I've written an HttpServlet which gets deployed to a Wildfly container in a WAR file. The servlet looks like this:
public class MyCallback extends HttpServlet {
#Inject
#Any
private Event<MyEvent> event;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
String eventName = request.getParameter("EVENT_NAME");
MyEvent e = new MyEvent(eventName);
event.fire(e);
}
}
I also created a beans.xml file (as detailed here) and placed it in my WEB-INF directory. Unfortunately, I still get a NullPointerException when the code tries to execute the event.fire(e) line, which suggests to me the injection isn't working.
What am I doing wrong?
Try adding at least one CDI bean (it doesn't have to do anything), e.g.
#ApplicationScoped
public class JustABean { }
I had similar issue on Wildfly beta, it seems without a single "normal" bean CDI engine just wouldn't kick in.
Also Wildfly ships CDI 1.1 where beans.xml is optional.

Injecting Stateless Local EJB (3.1) into #WebComponent in WebLogic 12c not working

I have a Java EE 6 War project containing the following:
An EJB declared as so (it's also a JAX-RS Service):
#Path("/booksList")
#Produces("application/json")
#Stateless
#LocalBean
#Local(BooksListEJB.class)
public class BooksListEJBImpl implements BooksListEJB
A WebComponent declared as so:
#WebServlet(urlPatterns="/initDbData")
public class DataInitListener extends HttpServlet {
#EJB
private BooksListEJB booksListEJB;
An empty beans.xml file in the WEB-INF folder
When I deploy it in WebLogic 12c, I get the following error:
<Warning> <weblogic.jaxrs.onwls.deploy.ejb.provider.EJBComponentProviderFactory> <BEA-000000> <An instance of EJB class com.shivandragon.jee6TestService.ejb.impl.BooksListEJBImpl could not be looked up using simple form name. Attempting to look up using the fully-qualified form name.
javax.naming.NameNotFoundException: While trying to look up comp/env/BooksListEJBImpl in /app/webapp/jee6Test-service-0.1-SNAPSHOT.war/2039754748.; remaining na
me 'comp/env/BooksListEJBImpl'
at weblogic.jndi.internal.BasicNamingNode.newNameNotFoundException(BasicNamingNode.java:1180)
at weblogic.jndi.internal.ApplicationNamingNode.lookup(ApplicationNamingNode.java:146)
at weblogic.jndi.internal.WLEventContextImpl.lookup(WLEventContextImpl.java:253)
at weblogic.jndi.internal.WLContextImpl.lookup(WLContextImpl.java:426)
at weblogic.jndi.factories.java.ReadOnlyContextWrapper.lookup(ReadOnlyContextWrapper.java:45)
Truncated. see log file for complete stacktrace
I've looked similar questions, and found the suggestion to add #ManagedBean to the servlet. Tried that but had the same error.
My question is:
Shouldn't this work, am I misusing some Java EE 6 directive/standard?
In EJB 3.1 have been added new Bean view - LocaBean. You can develop a bean without need implement any inerfaces. That beans view is "no-interface view", annotated with #LocalBean and injected by classname. There are beans that implemented some local interfaces and has "local view" and should be injected via local interface. In your code you mixed no-interface view bean and local view bean. You should delete the #LocalBean annotation as #Sam answered.
Updated
I test it on WebLogic Server 12.1.1.0.
Create a simple interface with one method:
package ejbrest;
public interface SessionEJBLocal {
public String hello();
}
Then create a EJB with the RESTful annotations:
package ejbrest;
// ... imports
#Path("/booksList")
#Produces("application/json")
#Stateless
#Local(SessionEJBLocal.class)
public class SessionEJBBean implements SessionEJBLocal {
public SessionEJBBean() {
}
#Override
#GET
public String hello() {
return "Hello, world";
}
}
The deployment descriptor, web.xml (you can see it does not have any servlet definitions):
<?xml version = '1.0' encoding = 'UTF-8'?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
</web-app>
You can create a servlet for the local bean injection demo:
package ejbrest;
// ... imports
#WebServlet(name = "DemoServlet", urlPatterns = { "/demoservlet" })
public class DemoServlet extends HttpServlet {
private static final String CONTENT_TYPE = "text/html; charset=UTF-8";
#EJB
private SessionEJBLocal ejb;
public void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
response.setContentType(CONTENT_TYPE);
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<head><title>DemoServlet</title></head>");
out.println("<body>");
out.println("<p>The servlet has received a GET. This is the reply: " +
ejb.hello() + "</p>");
out.println("</body></html>");
out.close();
}
}
After deployment you can try call your RESTful service by url:
http://[host]:[port]/[appcontext]/resources/booksList
Response:
Hello, world
Also, your demo servlet will be accessable by url:
http://[host]:[port]/[appcontext]/demoservlet
Response:
The servlet has received a GET. This is the reply:Hello, world
By default Oracle WebLogic Server use resources as link on Jersey servlet. Please read the official documentation for informs about all supported deployments variants.

Can't convert from #stateless to #stateful beans

I'm having a problem instantiating a stateful session bean on glassfish3.1.
A #ManagedBean (session scoped) of a JSF application used to use a #Local interface of a #Stateless session bean and everything worked fine.
Now I had to convert to a #Stateful bean and I'm getting an exception when I try to inject the stateful bean into the mannaged bean.
The code in question consists of the following 3 layers:
#ManagedBean
#SessionScoped
public class ShopBean {
private #EJB ShopAdminInterface sa;
...
}
#Local
public interface ShopAdminInterface {
.... some interfaces
}
#Stateful
public class ShopAdmin implements ShopAdminInterface {
#EJB CoreClassEAO s;
... some implementation
}
The CoreClassEAO presents an access layer to the database and looks like this:
#Stateful
public class CoreClassEAO {
#PersistenceContext
EntityManager em;
.... access to my persistence interface
}
In the last version, when the ShopAdmin and CoreClassEAO both were #Stateless beans everything worked perfectly. But now, injecting ShopAdminInterface throws an exception at me
Update: I narrowed down the problem: See my other question
Injecting #Stateful bean into another #Stateful bean

Inject a stateless EJB with #Inject into CDI Weld ManagedBean (JSF 1.2 EJB Application on jboss 6 AS)

Currently I am trying to inject a stateless EJB into a CDI managed controller on Jboss 6 AS Final. The controller is managed in the context an accessible from the JSF pages. If I inject the stateless bean with #EJB it is working. If I inject the stateless EJB with #Inject I get the following Exception:
My controller:
#Named("TestController")
public class TestController {
#Inject
private TestManagerLocal myTestManager;
...
}
}
My stateless bean:
#SuppressWarnings("unchecked")
#Stateless
public class TestManagerBean implements TestManagerLocal {
#PersistenceContext
private EntityManager em;
...
}
The Interface of the Bean is annotated with #Local.
If I try to call myTestManager I get the following exception:
WELD-000079 Could not find the EJB in JNDI: class
de.crud.org$jboss$weld$bean-jboss$classloader:id="vfs:$$$usr$local$jboss$server$default$deploy$test$ear"-SessionBean-TestManagerBean_$$_WeldProxy
THX a lot.
For those not having the luxury to change an ear to a war, I've found the following workaround:
Create an EJB in the war
Inject that EJB with the EJBs from the EJB module
Add CDI producer methods
Qualify #Inject with the qualifier for those producer methods:
Code:
// This bean is defined in the WEB module
#Stateless
public class EJBFactory {
#EJB
protected UserDAO userDAO;
// ~X other EJBs injected here
#Produces #EJBBean
public UserDAO getUserDAO() {
return userDAO;
}
// ~X other producer methods here
}
Now EJBs from anywhere in the EAR can be injected with:
// This bean is also defined in the web module
#RequestScoped
public class MyBean {
#Inject #EJBBean
private UserDAO userDAO; // injection works
public void test() {
userDao.getByID(...); // works
}
}
EJBBean is a simple standard qualifier annotation. For completeness, here it is:
#Qualifier
#Retention(RUNTIME)
#Target({TYPE, METHOD, FIELD, PARAMETER})
public #interface EJBBean {
}
The problem was, that I built and deployed my application as an ear. Weld is working when I deploy my application as an war including all EJBs.
Currently there are various problems arising from the fact that WARs in EAR-Deployments don't share the same classloader. See https://issues.jboss.org/browse/JBAS-8683 for the ongoing discussion in the JBoss-AS JIRA (and vote it up :-) )
UPDATE I found this information on how to disable separate classloaders, option 1 worked for me, but be extremely careful with this. The separation of classloaders hasn't been introduced for no reason, so apparently there are new problems on the road ahead...

Resources