Managed property value not updated when both beans are of same scope - jsf-2

I am seeing this strange behavior when using #ManagedProperty. I have 2 beans:
UserManager (SessionScoped)
#ManagedBean
#SessionScoped
public class UserManager extends BaseBean implements Serializable
{
private static final long serialVersionUID = 1861000957282002416L;
private User currentUser;
public String login()
{
// set value of currentUser after authentication
}
public User getCurrentUser() {
return currentUser;
}
public boolean isLoggedIn() {
return getCurrentUser() != null;
}
}
CartBean (ALSO SessionScoped)
...
import javax.faces.bean.ManagedProperty;
...
#ManagedBean
#SessionScoped
public class CartBean extends BaseBean implements Serializable
{
#ManagedProperty(value = "#{userManager.loggedIn}")
private boolean loggedIn;
public void updateCart(Movie selectedMovie)
{
if (!loggedIn) {
return;
}
System.out.println("UPDATE CART REQUEST");
int id = selectedMovie.getMovieID();
if (cart.containsKey(id)) {
cart.remove(id);
}
else {
cart.put(id, selectedMovie);
}
}
public void setLoggedIn(boolean loggedIn) {
this.loggedIn = loggedIn;
}
}
After logging in successfully, the value of loggedIn still remains false.
However, if I change the scope of CartBean to #ViewScoped, the value of loggedIn gets updated and I see the sysout.
As per my understanding and also after reading various articles, one can inject a managed bean or its property only if it is of the same or broader scope. But the "same scope" case does not seem to work in my code. What am I missing here?
I am using:
Mojarra 2.1.16
Spring 3.2
Hibernate 4.1
Tomcat 7.0.37

#ManagedProperty annotation can only provide static injection, which means that the annotated property will get injected when and only when the holding #ManagedBean is instantiated.
When you deploy your application, I believe your CartBean was referenced right at the beginning through things like the View cart button, etc. As a consequence, the injection took place too early and since the bean is #SessionScoped, you will carry the initial false value till the end of time :).
Instead of injecting only the boolean field, you should, instead, inject the whole UserManager bean:
#ManagedBean
#SessionScoped
public class CartBean extends BaseBean implements Serializable {
#ManagedProperty(value = "#{userManager}")
private UserManager userManager;
public void updateCart(Movie selectedMovie) {
if (!userManager.isLoggedIn()) {
return;
}
...
}
}

The solution is using Omnifaces it worked for me each time the value change you will get the new value
#ManagedBean
#ViewScoped
public class CartBean extends BaseBean implements Serializable {
private boolean loggedIn;
public void updateCart(Movie selectedMovie) {
loggedIn=Faces.evaluateExpressionGet("#{userManager.loggedIN}");
if (!userManager.isLoggedIn()) {
return;
}
...
}
}

Related

How to inject a bean into custom argument resolver?

Hello i use spring boot 1.3.2 version. I have a custom argument resolver which's name is ActiveCustomerArgumentResolver. Everything is great, resolveArgument method works fine but i can't initialize my service component which is of my custom arg. resolver. Is there a problem with lifecycle process? Here is my code:
import org.springframework.beans.factory.annotation.Autowired;
//other import statements
public class ActiveCustomerArgumentResolver implements HandlerMethodArgumentResolver {
#Autowired
private CustomerService customerService;
#Override
public boolean supportsParameter(MethodParameter parameter) {
if (parameter.hasParameterAnnotation(ActiveCustomer.class) && parameter.getParameterType().equals(Customer.class))
return true;
else
return false;
}
#Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
Principal userPrincipal = webRequest.getUserPrincipal();
if (userPrincipal != null) {
Long customerId = Long.parseLong(userPrincipal.getName());
return customerService.getCustomerById(customerId).orNull(); //customerService is still NULL here, it keeps me getting NullPointerEx.
} else {
throw new IllegalArgumentException("No user principal is associated with the current request, yet parameter is annotated with #ActiveUser");
}
}
}
Let the Spring create the resolver for you by making it a Component:
#Component
public class ActiveCustomerArgumentResolver implements HandlerMethodArgumentResolver {...}
Then inject the resolver into your WebConfig instead of simply using the new, like following:
#EnableWebMvc
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Autowired private ActiveCustomerArgumentResolver activeCustomerArgumentResolver;
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(activeCustomerArgumentResolver);
}
}
This is how i've solved the problem, not a generic one but helps me a lot:
#Configuration
#EnableAutoConfiguration
#ComponentScan
public class Application extends WebMvcConfigurerAdapter {
private static final Logger logger = LoggerFactory.getLogger(Application.class);
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(activeCustomerArgumentResolver());
}
#Bean
public ActiveCustomerArgumentResolver activeCustomerArgumentResolver() {
return new ActiveCustomerArgumentResolver();
}
}

Primefaces datatable with CDI Bean not paginating [duplicate]

I'm currently evaluating Java EE 6 / JSF 2.1 with RichFaces.
A bean which is declared as
#ManagedBean
#ViewScoped
Gets an ID set (to prepare e.g. a delete operation).
Via JSF a confirmation popup is displayed.
If the user confirms, the delete method is invoked and removes the row for which the ID was stored in step 1.
Since CDI beans don't have a ViewScope I tried to declare the bean as:
#Named
#ConversationScoped
Now the processing fails in step 3. because the value that was set in step 1 (checked that) is no longer available.
Do I have to use Conversation.begin() and Conversation.end() methods?
If so, where would be good place to invoke them?
If you can upgrade to JSF 2.2, immediately do it. It offers a native #ViewScoped annotation for CDI.
import javax.faces.view.ViewScoped;
import javax.inject.Named;
#Named
#ViewScoped
public class Bean implements Serializable {
// ...
}
Alternatively, install OmniFaces which brings its own CDI compatible #ViewScoped, including a working #PreDestroy (which is broken on JSF #ViewScoped).
import javax.inject.Named;
import org.omnifaces.cdi.ViewScoped;
#Named
#ViewScoped
public class Bean implements Serializable {
// ...
}
Another alternative is to install MyFaces CODI which transparently bridges JSF 2.0/2.1 #ViewScoped to CDI. This only adds an autogenerated request parameter to the URL (like #ConversationScoped would do).
import javax.faces.bean.ViewScoped;
import javax.inject.Named;
#Named
#ViewScoped
public class Bean implements Serializable {
// ...
}
If you really need to use #ConversationScoped, then you indeed need to manually begin and end it. You need to #Inject a Conversation and invoke begin() in the #PostConstruct and end() in the latest step of the conversation, usually an action method which redirects to a new view.
import javax.enterprise.context.Conversation;
import javax.enterprise.context.ConversationScoped;
import javax.inject.Named;
#Named
#ConversationScoped
public class Bean implements Serializable {
#Inject
private Conversation conversation;
// ...
#PostConstruct
public void init() {
conversation.begin();
}
public String submit() {
// ...
conversation.end();
return "some.xhtml?faces-redirect=true";
}
}
See also:
How to choose the right bean scope?
I think you can benefit from CDI extension to create your own scope so you can implement the context and use the #NormalScope.
CDI fires an event AfterBeanDiscovery after each bean call
You can use CDI extension to #Observes this event and add your context implementation
In your scope implementation you can :
Use Contextual to get your bean by its name from FacesContext ViewRoot Map and return it after each ajax call back
Use CreationalContext if the bean name from first step is not found to create it in the FacesContext ViewRoot Map
For a more in-depth explanation, I recommend this link : http://www.verborgh.be/articles/2010/01/06/porting-the-viewscoped-jsf-annotation-to-cdi/
Inject the conversation into your bean and in the #PostConstructor method start the conversation if the conversation is transient.
And after deleting the record, end your conversation and navigate to your destination page. When beginning a conversation. Here is an example
public class BaseWebBean implements Serializable {
private final static Logger logger = LoggerFactory.getLogger(BaseWebBean.class);
#Inject
protected Conversation conversation;
#PostConstruct
protected void initBean(){
}
public void continueOrInitConversation() {
if (conversation.isTransient()) {
conversation.begin();
logger.trace("conversation with id {} has started by {}.", conversation.getId(), getClass().getName());
}
}
public void endConversationIfContinuing() {
if (!conversation.isTransient()) {
logger.trace("conversation with id {} has ended by {}.", conversation.getId(), getClass().getName());
conversation.end();
}
}
}
#ConversationScoped
#Named
public class yourBean extends BaseWebBean implements Serializable {
#PostConstruct
public void initBean() {
super.initBean();
continueOrInitConversation();
}
public String deleteRow(Row row)
{
/*delete your row here*/
endConversationIfContinuing();
return "yourDestinationPageAfter removal";
}
}
There is a project which holds an extentions to the Java EE stack features: DeltaSpike. It is a consolidation of Seam 3, Apache CODI. Above others, it includes the #ViewScoped into CDI. This is an old article and by now it has reached version 1.3.0
You can use:
import javax.annotation.PostConstruct;
import javax.faces.view.ViewScoped;
import javax.inject.Named;
#Named
#ViewScoped
public class PageController implements Serializable {
private String value;
public void setValue(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public void execute() {
setValue("value");
}
#PostConstruct
public void init() {
System.out.println("postcontructor");
}
}

The JSR 303 bean validation, The extended ConstraintValidator cannot use the CDI

I've tried to learn the JSF 2.0 with bean validation at the class level as the following: -
The utility
#Singleton
public class MyUtility {
public boolean isValid(final String input) {
return (input != null) || (!input.trim().equals(""));
}
}
The constraint annotation
#Retention(RetentionPolicy.RUNTIME)
#Target({
ElementType.TYPE,
ElementType.ANNOTATION_TYPE,
ElementType.FIELD
})
#Constraint(validatedBy = Validator.class)
#Documented
public #interface Validatable {
String message() default "Validation is failure";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
The constraint validator
public class Validator extends ConstraintValidator<Validatable, MyBean> {
//
//----> Try to inject the utility, but it cannot as null.
//
#Inject
private MyUtility myUtil;
public void initialize(ValidatableconstraintAnnotation) {
//nothing
}
public boolean isValid(final MyBean myBean,
final ConstraintValidatorContext constraintContext) {
if (myBean == null) {
return true;
}
//
//----> Null pointer exception here.
//
return this.myUtil.isValid(myBean.getName());
}
}
The data bean
#Validatable
public class MyBean {
private String name;
//Getter and Setter here
}
The JSF backing bean
#Named
#SessionScoped
public class Page1 {
//javax.validation.Validator
#Inject
private Validator validator;
#Inject
private MyBean myBean;
//Submit method
public void submit() {
Set<ConstraintViolation<Object>> violations =
this.validator.validate(this.myBean);
if (violations.size() > 0) {
//Handle error here.
}
}
}
After running I've faced the exception as java.lang.NullPointerException at the class named "Validator" at the line return this.myUtil.isValid(myBean.getName());. I understand that the CDI does not inject my utility instance. Please correct me If I'm wrong.
I'm not sure if I'm doing something wrong or it is a bean validation limitation. Could you please help to explain further?
Your right, Hibernate Constraint Validator is not registered as a CDI-Bean by default (and though cannot receive dependencies).
Just put the Seam-Validation module on your classpath, and everything should run fine.
BTW: studying the source-code of the module is an excellent example of the elegance and simplicity of CDI extension. It's doesn't need more than a few dozens lines of code to bridge from CDI to hibernate validations...

Lazy loading exception when using JSF Converter (refering to a collection)

This is my first post after many research on this problem.
This example is running under Jboss 7.1 with seam 3.1 (solder + persistence + faces) with seam managed persistence context
I'm facing a problem, the classical failed to lazily initialize a collection, no session or session was closed: org.hibernate.LazyInitializationException: failed to lazily initialize a collection, no session or session was closed when using a converter on Entity beans. The aim is to stay 100% Object oriented, by reusing the JPA model.
in beans.xml, org.jboss.seam.transaction.TransactionInterceptor is activated
Entity beans :
#Entity
public class Member implements Serializable {
#Id
#GeneratedValue
private Long id;
private String name;
private String email;
#Column(name = "phone_number")
private String phoneNumber;
#ManyToMany
private List<Statut> listeStatut = new ArrayList<Statut>();
// getters, setters, hashcode, equals
}
#Entity
public class Statut implements Serializable {
#Id
#GeneratedValue
private Long id;
private String name;
#ManyToMany(mappedBy="listeStatut")
private List<Member> members = new ArrayList<Member>();
// getters, setters, hashcode, equals
}
The JSF page :
<h:form>
<h:selectManyCheckbox id="stat" value="#{memberModif.member.listeStatut}">
<f:converter converterId="statutConverter"/>
<f:selectItems value="#{memberModif.statutsPossibles}" var="statut" itemValue="#{statut}" itemLabel="#{statut.name}" />
</h:selectManyCheckbox>
<h:commandLink id="register" action="#{memberModif.modifier()}" value="Modifier">
<f:param name="cid" value="#{javax.enterprise.context.conversation.id}"/>
</h:commandLink>
</h:form>
The backing bean (I tried with ConversationScoped after SessionScoped --> same problem)
#ConversationScoped
#Named
public class MemberModif implements Serializable {
private static final long serialVersionUID = -291355942822086126L;
#Inject
private Logger log;
#Inject
private EntityManager em;
#Inject Conversation conversation;
private Member member;
#SuppressWarnings("unused")
#PostConstruct
private void init() {
if (conversation.isTransient()) {
conversation.begin();
}
}
public String modifier() {
em.merge(member);
}
public Member getMember() {
if (member == null) {
member = em.createQuery("from Member m where m.id=:id",Member.class).setParameter("id", new Long(0)).getSingleResult();
}
return member;
}
public List<Statut> getStatutsPossibles() {
return em.createQuery("from Statut", Statut.class).getResultList();
}
}
And the converter (strongly inspired by seam ObjectConverter) :
#FacesConverter("statutConverter")
public class StatutConverter implements Converter, Serializable {
final private Map<String, Statut> converterMap = new HashMap<String, Statut>();
final private Map<Statut, String> reverseConverterMap = new HashMap<Statut, String>();
#Inject
private transient Conversation conversation;
private final transient Logger log = Logger.getLogger(StatutConverter.class);
private int incrementor = 1;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (this.conversation.isTransient()) {
log.warn("Conversion attempted without a long running conversation");
}
return this.converterMap.get(value);
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (this.conversation.isTransient()) {
log.warn("Conversion attempted without a long running conversation");
}
if (this.reverseConverterMap.containsKey(value)) {
return this.reverseConverterMap.get(value);
} else {
final String incrementorStringValue = String.valueOf(this.incrementor++);
this.converterMap.put(incrementorStringValue, (Statut)value);
this.reverseConverterMap.put( (Statut)value, incrementorStringValue);
return incrementorStringValue;
}
}
}
Please note that I put this converter here to avoid you searching over the net for the seam implementation, but it is the same as using <s:objectConverter/> tag instead of <f:converter converterId="statutConverter"/>
Any help would be greetly appreciated.
You should access the objects in the same transaction. If you are sure you are doing that already, you could try getting the entitymanager by looking it up in the context instead of injecting it. Ive had a simular problem which was resolved that way. You can also initialize the collection in the transaction when you first got your reference to it.
Hibernate.initialize(yourCollection);
Take a look at this: selectManyCheckbox LazyInitializationException on process validation
Try:
<f:attribute name="collectionType" value="java.util.ArrayList" />;
on your <h:selectManyCheckbox>

#ManagedProperty annotated type returns null

I have this Service bean:
#Stateless
public class BookService
{
#PersistenceContext(unitName="persistentUnit")
protected EntityManager entityManager;
public BookModel find(Long id) {
return entityManager.find(BookModel.class, id);
}
}
And the backing bean for the Facelet page is:
#ManagedBean(name = "bookBean")
#RequestScoped
public class BookBean implements Serializable
{
#EJB
private BookService bookService;
#ManagedProperty(value="#{param.id}")
private Long id;
private DataModel<BookModel> books;
private BookModel currentBook;
#PostConstruct
public void init() {
if (id == null) {
// UPDATE: Retrieve a list of books.
} else {
// UPDATE: id shouldn't be null here.
// Get detail info about a book using the id
currentBook = bookService.find(id);
}
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public BookModel getCurrentBook() {
return currentBook;
}
public void setCurrentBook(BookModel currentBook) {
this.currentBook = currentBook;
}
}
Why is the value of id always returns null even though the URL returned as bookedit.jsf?id=5418 I don't understand this.
Also, I find the EntityManager#find method quite restrictive in that it only accept a primary key value as the second parameter. What if I want to pass a [hashed] value instead of the primary key. How can I do this with the EntityManager#find method?
P.S. I notice the EntityManager#find requirement is the same for both OpenJPA and EclipseLink implementations. Hmm...
I just tried this in one of my managed beans, and it is working. Here's the relevant code, it's basically the same as yours:
#ManagedBean
#RequestScoped
public class TestBean {
#ManagedProperty(value = "#{param.id}")
private Long prop;
#PostConstruct
public void init() {
System.out.println(prop);
// prints 1234 if I go to the url with http://localhost/page.jsf?1234
}
public Long getProp() {
return prop;
}
public void setProp(Long prop) {
this.prop = prop;
}
}
I'm running this on glassfish 3.1.1. The only thought I had is maybe the injected EJB is somehow messing up the request scope in the ManagedBean?

Resources