retrieve a #Named managed bean from session scope - java

i've a question about the new JEE6 CDI specification, in particular the use of #Inject in conjunction with #Named annotations that seems to have replaced the jsf specific #ManagedBean (for register resource) and #ManagedProperty (for injection) annotations.
Assuming you have a bean class "User" annotated #ManagedBean #SessionScoped and an object of this class is injected in any bean using #ManagedProperty then when this object is created and injected it is also put in session and i could access the session (for example in a filter) and retrieve object using:
User user = (User)((HttpServletRequest) request).getSession().getAttribute("user");
So, if I try the same logic (in glassfish 3.1.2) using #Named #SessionScoped and then #Inject I can confirm that there is no such object of type User in session, but this is what i find in session:
attribute_name: org.jboss.weld.context.conversation.ConversationIdGenerator
attribute_value: org.jboss.weld.context.conversation.ConversationIdGenerator#b374765
attribute_name: org.jboss.weld.context.ConversationContext.conversations
attribute_value: {}
where i'm doing wrong?!

If you annotate a bean with #SessionScoped, you are basically binding its lifecycle to the HttpSession. This does not mean that the bean is physically injected into the session-object.
Assuming you have a bean class "User" annotated #ManagedBean
#SessionScoped and an object of this class is injected in any bean
using #ManagedProperty then when this object is created and injected
it is also put in session and i could access the session (for example
in a filter) and retrieve object using:
Very important: don't mix JSF and CDI annotations. Make all beans you want to access from within a JSF template #Named, and use CDI's #Inject and #...Scoped, that's sufficient.
To answer your question:
#SessionScoped
public class User { ... }
#RequestScoped
public class SomeController {
#Inject
User user
...
}
This will create User when the session starts and inject it into SomeController when SomeController is instantiated (with a new request, in this example).
Have a look at the Weld documentation to get a more complete start with that topic...

Related

Cannot find EJB beanName on deploy

Hi #All :) I have a strange problem here. I want to inject UserBean managed entity in a class with the #EJB(beanName="user"). If I don't use the beanName attribute I get the following error message:
org.jboss.as.server.deployment.DeploymentUnitProcessingException: No component found for type 'defaultPackage.UserBean' with name null
If I use it like so #EJB(beanName="user") UserBean user; I get this:
org.jboss.as.server.deployment.DeploymentUnitProcessingException: No component found for type 'defaultPackage.UserBean' with name user
What am I doing wrong ?
Here is the UserBean and how I inject it:
UserBean
// removed imports
#ManagedBean(name="user")
#Local
#SessionScoped
#Entity
#Table(name="t_user")
public class UserBean implements Serializable, UserInterface{
// fields (#Columns, etc.)
// setters / getters
}
UserBeanUser
// more removed imports
#ManagedBean(name="userUsed")
#Stateful
#Local
public class UserOfUserBean implements Serializable, Userable {
private #EJB(beanName="user") UserBean user;
}
The interfaces are empty and are marked as #Local.
I'm sorry, but this is wrong on so many levels that it's hard where to begin.
To start, the #Local annotation is useless on the code you show. It's used to mark an interface as a business interface for an EJB, or point to such an interface.
Then, the entity UserBean should probably not be scoped and a managed bean. These annotations are for letting the bean container manage the life-cyle of a bean, but in case of an entity it's the entity manager that is responsible for that.
It's not clear whether you used the JSF or Java EE ManagedBean annotation here, but the latter is never needed and the first shouldn't be needed as per the above explanation.
Additionally, per the typical conventions consider naming an entity just User and don't let it implement an interface.
UserOfUserBean is even harder to decipher. Is this bean supposed to be a business service or a (JSF) backing bean? If it's a backing bean, you don't need the #Stateful annotation and otherwise you don't need the #ManagedBean one.
Then, the major culprit of your question: the #EJB annotation can't inject non-EJB beans. JPA entities (#Entity) and JSF managed beans (#javax.faces.bean.ManagedBean) are not EJB beans.
Furthermore, the beanName attribute of #EJB does not correspond to the name attribute in #ManagedBean. The bean name is an EJB internal thing used to disambiguate an injection if an interface is implemented by multiple beans and has no relation to either the JSF managed bean name or the Java EE managed bean name. (not applicable to your question, but there is a relation between CDI's #Named and JSF's managed bean name)
Finally, entities nor backing beans need empty interfaces that are marked as #Local.
So, your entity should become something like:
#Entity
#Table(name="t_user")
public class User implements Serializable {
// fields (#Columns, etc.)
// setters / getters
}
And you should forget about injecting the entity the way you did. If you want to make it available in the HTTP session, put it there manually (e.g. after logging in). See this answer for a more elaborate explanation on how to do that: https://stackoverflow.com/a/6355064/472792
Assuming your UserOfUserBean was a backing bean, it would then become something like this with CDI:
#Named(name="userUsed")
public class UserOfUserBean {
#Inject
#LoggedIn // see answer from jan groth
private User user;
}
or with JSF annotations only:
#ManagedBean(name="userUsed") // from javax.faces.bean, not javax.annotation
public class UserOfUserBean {
#ManagedProperty("#{user}")
private User user;
// Mandatory setter for JSF-native injection
public void setUser(User user) {
this.user = user;
}
}
Some additional resources:
Contexts and Dependency Injection in Java EE 6
How do CDI and EJB compare? interact?

Scoped bean: inject one into another

How to inject a session scoped bean into another session scoped bean without proxy?
#Component
#Scope("session")
class Foo {
#Inject Bar bar;
}
#Component
#Scope("session")
class Bar {
}
It reports error "No matching bean". Though a TARGET_CLASS scope-proxy could resolve this problem, but why do I need a proxy for same scoped beans?
My guess is - because at the injection point spring doesn't distinguish injected beans depending on the scope of the current bean. It needs a proxy to fetch the target bean (from the session in this case), disregarding the scope of the bean where it's injected into.

Calling a CDI session scoped producer method from an EJB stateless session bean

I want to inject the current user using #Inject #Current User across all layers (i.e. web layer, EJB layer). In order to do this, I have the following CDI Producer method:
#Named
#SessionScoped
public class UserController {
#Resource SessionContext sessionContext;
#EJB UserDao userDao;
#Produces #Current
public User getCurrentUser() {
String username = sessionContext.getCallerPrincipal().getName();
User user = userDao.findByUsername(username);
}
}
#Qualifier
#Target({TYPE, METHOD, PARAMETER, FIELD})
#Retention(RUNTIME)
public #interface Current{}
Now, I want to inject the current user into an EJB stateless session bean as follows:
#Stateless
public class SomeBackendService {
#Inject #Current
private User user;
}
My question: Is the current user object always re-injected after the session changes, because the dependencies of a stateless session bean are normally injected once at creation time and the bean may be pooled and used across different sessions?
Although I haven't tried this exact situation, in CDI beans are normally not re-injected. Instead, a proxy is injected that is aware of its context.
Via this mechanism, it's possible to inject say a session scoped bean in an application scoped bean. Every user of the application scoped bean goes to the same bean and the same proxy, but the proxy will then dynamically resolve calls on it to a different bean for each user.
So even though the scope of #Stateless is basically 'application', it would be possible that the proxy that represents User in your `SomeBackendService' still delegates to the correct session scoped version.
p.s.
If with layers you actually mean modules as in web and EJB modules that are part of an EAR, it's going to be a little bit more complicated, as CDI doesn't always works as expected between modules (especially in JBoss AS). This is partly due to the ambiguity of what an 'application' and thus the application scope is within an EAR.
Yes, to each business method called the container will be re-injected all dependencies of your SLSB. Here is text that guarantees this in EJB 3.1 specification:
"If a session bean makes use of dependency injection, the container injects these references after the bean instance is created, and before any business methods are invoked on the bean instance." - Section 4.3.2
I had this doubt too and I posted a question explaining this situation
here
By design, your stateless session bean should not have a state "User", it's stateless by all means.
If you want your EJB to have states, then use #Stateful instead.

Inject a Entity into a ViewScoped Bean

I am new to CDI and want to use this for a JSF2 application. The class MyUser is a simple #Entity-Bean and a object is created in a #PostConstruct method in bean:
#Stateful
#Named #javax.faces.bean.SessionScoped
public class UserBean implements Serializable
{
#Named
private MyUser user;
//setter and getter
//#PostConstruct
}
Accessing the user in a JSF pages works like a charm: #{user.lastName}. But now I want to access this object from other beans, e.g. in this #ViewScopedBean:
#Named #javax.faces.bean.ViewScoped
public class TestBean implements Serializable
{
#Inject private MyUser user;
}
I want the current (logged in) MyUser user to be available in a couple of other beans, but I'm not sure how to do this. Simply #Injecting it did not work (and I'm pretty sure this would be a litte bit to simple).
13:56:22,371 ERROR [org.jboss.kernel.plugins.dependency.AbstractKernelController]
Error installing to Start: name=vfs:///Applications/Development/
jboss-6.0.0.Final/server/default/deploy/test.ear_WeldBootstrapBean state=Create:
org.jboss.weld.exceptions.DeploymentException: WELD-001408 Unsatisfied
dependencies for type [MyUser] with qualifiers [#Default] at injection
point [[field] #Inject private test.controller.mbean.TestBean.user]
What is the best approach to access the user from other beans? JSF1.2 style code like UserBean bean = (UserBean)FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get("UserBean"); seems seems to be old fashioned!
First of all: You don't want to directly inject entities. Entities are pretty independently controlled by the ORM-framework, and have their own life cycle. Don't use them as managed beans.
According to this definition, JPA
entities are technically managed
beans. However, entities have their
own special lifecycle, state and
identity model and are usually
instantiated by JPA or using new.
Therefore we don't recommend directly
injecting an entity class. We
especially recommend against assigning
a scope other than #Dependent to an
entity class, since JPA is not able to
persist injected CDI proxies.
See here for details.
To answer your question: You cannot "outject" something like an (authenticated) user, even though this was possible in Seam 2, the whole proxy mechanism of CDI doesn't allow this anymore. What you need to do is the following:
Write a managed bean which handles the authentication and put it in the correct scope (probably session scope).
If login succeeds, use an attribute of this bean to store the authenticated user.
Use a producer method (probably with a qualifier like #LoggedIn) to make the user availabe in your application
Inject the user like this:
#Inject
#LoggedIn
private User user
That's the CDI-way ;-)
Is the #Inject ed bean a #Named bean, too?
If it is, has the MyUser bean a lesser scope than the TestBean. Remember that a #ViewScoped bean's managed properties have to be #ViewScoped, #SessionScoped or #ApplicationScoped
CDI does not specify a #ViewScoped annotation. This is a JSF2 annotation. The only possible annotations allowed are : #RequestScoped, #SessionScoped, #ApplicationScoped, #Dependent and #ConversationScoped. The first three are the only JSF scopes allowed by CDI.
If you need to support the #ViewScope annotation, you'll need write it yourself. Luckily, someone else has done this before.

is it not allowed to implement a single local interface by two stateless beans?

I am getting following exception when a Local Interface is implemented by two Stateless beans, in which one having normal functionality and other having some enhanced functionality in it.
java.lang.RuntimeException: could not
resolve global JNDI name for #EJB for
container UserBean: reference class:
org.app.SecurityServiceLocal ejbLink:
duplicated in Some.jar
Finally I came to know why I am getting this exception
I have used #EJB annotation to inject a Stateless bean into another Stateless bean Name UserBean with following code
#Stateless(name="UserBean")
#EJB(name="app/SecurityService",
beanInterface=SecurityServiceLocal.class)
public class UserBean implements UserRemote{
}
If you check the injection details I was injecting SecurityServiceLocal, which was implemented by two Stateless bean classes name SercurityServiceBean and SecurityServiceEnhaBean. So, container is in ambiguity state to decide which bean to inject in as both are implementing same interface.
This can be resolved by specifying some more information like beanName property value in #EJB annotation. There you need to provide which stateless bean class needs to be injected by using bean name(declared at that bean level (or) in ejb-jar.xml). check the code to identify the change in the injection mapping
#Stateless(name="UserBean")
#EJB(name="app/SecurityService",
beanInterface=SecurityServiceLocal.class,
beanName="SecurityServiceEnha")
public class UserBean implements UserRemote{
}
It's hard to say for sure without seeing code. A good first step would be to use the optional mappedName="" attribute on your session beans' annotion to give each a unique JNDI name. You'll have to use an equivalent mappedName attribute in your client to make sure you are using the bean that you intent.

Categories