JSF 2 reusing the validation defined in the JPA entities? - jsf

Let's start with an example :
In my JPA entity
public class User {
#Pattern("^([0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*#([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,9})$", message="invalidEmailResourceBundleKey")
private String email;
#Min(5, message="minimumResourceBundleKey")
private int age;
...
}
In my JSF Bean
public class UserBean {
private User user;
// do i have to redefine it here, since it's already a part of the user ?
##Pattern("^([0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*#([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,9})$")
public String getEmail() {
return user.getEmail();
}
public void setEmail(String s) {
user.setEmail(s);
}
// do i have to redefine it here, since it's already a part of the user ?
#Min(5, message="minimumResourceBundleKey")
public int getAge() {
return user.getAge();
}
public void setAge(int age) {
user.setAge(age);
}
}
Is it possible to reuse the the validations for the entities for the JSF beans that actually delegates the method calls to the entities, so that i dont have to redefine the bean validations on the JSF beans ?
Can i even extend the reusing to the level of the error message in resource bundle, and whether the message can be parameterized with {0} etc like the usual ? I wonder if there's any example on the web for this, since i've been unable to find none.
Please share your thoughts on this ..
Thank you !

You don't need to redefine them if you don't unnecessarily flatten the bean properties. Just have a getUser() instead.
public class UserManager {
private User user;
public User getUser() {
return user;
}
}
And bind to the properties of the JPA entity directly.
<h:inputText value="#{userManager.user.email}" />
<h:inputText value="#{userManager.user.age}" />
Unrelated to the concrete problem, your email regex will fail for internationalized domain names (IDN) which are introduced last year. I'd fix the regex to not only accept latin characters. See also this answer for an example.

Related

Cant access property of managed bean from another managed bean

I want to access the property of a #SessionScoped bean in another bean using #ManagedProperty. In short, I want to access the name property of firstBean in secondBean.
#ManagedBean
#SessionScoped
public class FirstBean implements Serializable{
private String name;
//...other attributes
//...constructor
public String getSelectedModel() {
return selectedModel;
}
public void setSelectedModel(String selectedModel) {
this.selectedModel = selectedModel;
}
//other getters&setters
}
And second bean:
#ManagedBean
#SessionScoped
public class SecondBean implements Serializable{
#ManagedProperty(value="#{firstBean}")
private FirstBean firstBean
public SecondBean() {
System.out.println(firstBean.getName());
}
public IndexBean getFirstBean() {
return firstBean;
}
public void setFirstBean(FirstBean firstBean) {
this.firstBean = firstBean;
}
When I run this, I always get NullPointerException on System.out.println(firstBean.getName()); in the constructor of second bean, which seems to mean that I need to create a new instance of firstBean.
But strangely, when I commented out this line, I can do something like this with no errors, which means that firstBean is actually a property of secondBean.
<h:outputText value="#{secondBean.firstBean.name}" />
What's the problem here?
It's not possible to access an injected dependency in the constructor. You're basically expecting that Java is able to do something like this:
SecondBean secondBean; // Declare.
secondBean.firstBean = new FirstBean(); // Inject.
secondBean = new SecondBean(); // Construct.
It's absolutely not possible to set an instance variable if the instance is not constructed yet. Instead, it works as follows:
SecondBean secondBean; // Declare.
secondBean = new SecondBean(); // Construct.
secondBean.firstBean = new FirstBean(); // Inject.
Then, in order to perform business actions based on injected dependencies, use a method annotated with #PostConstruct. It will be invoked by the dependency injection manager directly after construction and dependency injection.
So, just replace
public SecondBean() {
System.out.println(firstBean.getName());
}
by
#PostConstruct
public void init() { // Note: method name is fully to your choice.
System.out.println(firstBean.getName());
}

Initialization of List in a JSF Managed bean

I' have a question about initialization of List in the POJO as it follows the next code:
public class Person {
//other fields...
private List<String> friends=new ArrayList<>();
public List<String> getFriends() {
return friends;
}
public void setFriends(List<String> friends) {
this.friends = friends;
}
}
OR is it better like this and have initalization in other class(like for example Bean(JSF))
public class Person {
//other fields...
private List<String> friends;
public List<String> getFriends() {
return friends;
}
public void setFriends(List<String> friends) {
this.friends = friends;
}
}
So my question is what approach is better?
If it's a managed bean as you say, you should do this in a method annotated with #PostConstruct
public class Person {
private List<String> friends;
#PostConstruct
public void init(){
friends = new ArrayList<String>();
}
//getter and setter...
}
The practice of doing any initialization in the getter and setter is generally frowned upon within the context of JSF. See Why JSF calls getters multiple times
Also, per the API for #PostConstruct, the contract specifies safety features and guarantees that if an exception is thrown in a method annotated as such, the bean should not be put into service. There are no such guarantees on a plain constructor.
In a managed bean, injection happens immediately after construction. This means that any operations you're carrying out in the constructor cannot depend on any injected resources (via #ManagedProperty). Whereas in a #PostConstruct method, you'll have access to all the resources declared on the managed bean
EDIT: It's important to note that there can be only one #PostConstruct for any #ManagedBean, so all important initializations should happen in there.
It's also worthwhile to note that, while the #PostConstruct method is the ideal place to initialize a backing bean variable/List, there are implications regarding the scope of the managed bean
#RequestScoped: In a managed bean with this annotation, the method will be called per submit of the JSF view concerned. A #RequestScoped bean is destroyed and recreated with every request, The implication of this is that depending on your setup, the list initialized in the #PostConstruct may be reset to empty or default values during each request. Under certain circumstances, conversion errors may occur as a result of the re-initialization of the list mid-JSF request.
#ViewScoped: In a managed bean with this annotation, you're guaranteed to have the #PostConstruct method run once, if and only if you're dealing with the same instance of the #ViewScoped bean. If the viewscoped bean is destroyed and recreated, the #PostConstruct method will run again.
#SessionScoped: A bean with this annotation is created once and stays alive until the user's HTTP session ends. In this scenario, the #PostConstruct method is guaranteed to run once and only once until the bean is destroyed
See also
https://stackoverflow.com/a/3406631/1530938
I would suggest this:
public class Person {
//other fields...
private List<String> friends=new ArrayList<>();
// returns a copy to protect original list
public List<String> getFriends() {
Collections.unmodifiableList(new ArrayList<>(friends));
}
public void addFriend(String> friend) {
this.friends.add(friend);
}
public void addFriends(List<String> friends) {
this.friends.addAll(friends);
}
}
In my opinion it would be best to handle that in the constructors. If a default constructor is used, initialize the list in the constructor.
public Person() {
friends = new ArrayList<>();
}
If a constructor which accepts parameters is used, let the calling class pass in a list.
public Person(ArrayList<> friends) {
this.friends = friends;//friends
}
My suggestion, add a null check in the getter:
public class Person {
//other fields...
private List<String> friends;
public List<String> getFriends() {
if (this.friends == null) friends = new ArrayList<String>();
return friends;
}
}
But also notice I have omitted the setter. Instead, in any client code, call like this:
personInstance.getFriends().add("Some Item");
Or if you have a full list to add:
personInstance.getFriends().addAll(someStringCollection);
It depends. Usually first way preferable because you may want to add something to collection later. If you won't know was your collection initialized or not you must check it every time.

#ManagedProperty does not reflect changes and keeps returning null

I'm trying to inject the value of one sessionscoped bean into a viewscoped bean but it keeps returning null, here's a snippet:
import javax.faces.application.FacesMessage;
import javax.faces.bean.SessionScoped;
import javax.faces.bean.ManagedBean;
import javax.faces.context.FacesContext;
//Class for managing the current logged-in user
#ManagedBean(name="user")
#SessionScoped
public class User implements Serializable{
private String userName;
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserName() {
return this.userName;
}
And it's used in:
#ManagedBean(name="databrowser")
#ViewScoped
public class dataBrowser implements Serializable {
private List<UploadData> dataItems;
private SelectItem[] dataTypeOptions, qualityOptions, accessOptions;
private UploadData selectedData;
private String filelocation;
#ManagedProperty(value="#{user.userName}")
private String userName;
public String getUserName() {
return this.userName;
}
dataBrowser is used to populate a Primefaces datatable, when it's called userName is null and I'm not sure why.
Recently I have problem with injecting nested managed bean properties by #ManagedProperties too. Once injected it never changed. I did a workaround by evaluating EL in getter instead of injecting it.
Try that:
public String getUserName() {
FacesContext context = FacesContext.getCurrentInstance();
return (String) context.getApplication().evaluateExpressionGet(context,"#{user.userName}", String.class);
}
You can also try injecting entire user bean and get userName field from it in getter.
With all setters/getters in place, I was having the same problem (null reference to user) because of missing empty constructor in User class.
In the example you provided, the dataBrowser and user beans are instantiated before constructing the table, so referencing #{dataBrowser.userName} should already find the userName #ManagedProperty correctly injected (not being a #PostConstruct problem).
I just came across the same problem, and found out by chance, that it is not working, if I try with firefox (actually icedove under linux), but well working, if I try with the eclipse build-in browser.
Even so this does not make sense to me, have you tried with different browsers already?
michal777's answer is very well working. I have extended it to this:
#ManagedProperty("#{nameBean}")
private NameBean nameBean;
public NameBean getNameBean() { return nameBean; }
public void setNameBean(NameBean nameBean) { this.nameBean = nameBean; }
public NameBean getNameBean_Workaround() {
FacesContext context = FacesContext.getCurrentInstance();
return (NameBean) context.getApplication().evaluateExpressionGet(context,"#{nameBean}", NameBean.class);
}
and later on:
if (nameBean != null) {
nameBean.setName("achsooo");
}
else {
getNameBean_Workaround().setName("achsooo2222");
}
Now, in the eclipse browser "achsooo" gets set, and in icedove "achsooo2222" gets set.
#{user.userName} is interpreted by JSF as getUser().getUserName()
So it is better to have a #ManagedProperty of type User, with its getter/setter methods getUser/setUser. With that you can access the user's name by #{user.userName}.
I had this problem, and the problem was actually twofold. (Note also that #ManagedProperty will only ever work in a #ManagedBean class and if that #ManagedProperty class is of the same or lesser scope (application, session, view, request, etc.).) Here is how I fixed it:
Problem 1: JSF is stupid and doesn't handle #ManagedProperty injection properly in abstract classes.
Solution:
Make every class that uses #ManagedProperty be annotated with #ManagedBean.
Make every abstract class that uses the property not be annotated with #ManagedProperty and instead only provide abstract getter and setter methods that non-abstract classes will each override.
Use the abstract class's getter method instead of the #ManagedProperty itself in abstract classes.
Problem 2: JSF is stupid and doesn't handle #ManagedProperty injection properly in #ManagedBean classes not created by JSF (i.e. you are creating these classes yourself using new).
Solution options:
Let JSF create the class that uses the #ManagedProperty.
Use the following code:
MyClass example = Utils.getELValue("EL Expression Goes Here", MyClass.class);
public static <T> T getELValue(final String elName, final Class<T> clazz) {
FacesContext fc = FacesContext.getCurrentInstance();
return (T) fc.getApplication().getELResolver().getValue(fc.getELContext(), null, elName);
// Potential (untested) alternative:
// ((HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest()).getSession().getAttribute("")
}

JSF converter Validation Error: value is not valid for SelectOneMenu UIComponent [duplicate]

This question already has answers here:
Validation Error: Value is not valid
(3 answers)
Closed 6 years ago.
I am using the managedBean userHome in requestScope, in which the entity 'user' is going to be persist.
The user has the leader column which is mapped in ManyToOne relation.My Code looks like this
#ManagedBean
#RequestScoped
public class UserHome {
private User user = new User();
// Getters and Setters
private List<SelectItem> selectItems = new ArrayList<SelectItem>();
public UserHome() {
for(User user: availableLeaders) {
selectItems.add(new SelectItem(user.getName(), user));
}
}
public void persis();
}
User.java
public class User {
#Id
#Column
private Integer id;
#Column
privat String name;
#ManyToOne
private User leader;
}
I am trying to get the value of this leader through h:selectOneMenu like this
<h:selectOneMenu value="#{userHome.user.leader}" converter="userConverter">
<f:selectItems value="#{userHome.selectItems}"/>
</h:selectOneMenu>
My converter looks like this
#FacesConverter(forClass = User.class, value="userConverter")
public class UserConverter implements Converter {
private Map<String, User> userValues = new HashMap<String, User>();
public UserConverter() {
init();
}
#Override
public Object getAsObject(FacesContext context, UIComponent component,
String value) {
return userValues.get(value);
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
System.out.println("RAJASEKARAN "+value);
return ((User)value).getName();
}
public void init() {
UserHome userHome = new UserHome();
for(User user:userHome.availableLeaders()) {
userValues.put(user.getName(), user);
}
}
}
While try to save the user I am getting the error UserEdit:j_idt18: Validation Error: Value is not valid
Adding to BalusC's answer: after the postback, you need to make sure that the User instances are either exactly the same ones as you used for rendering the select items, or that you implement equals for your User class.
The code doesn't show where availableLeaders comes from, but if this is fetched from a DB on-demand, then the converter will not convert to the exact same object instance that's in the list that JSF resolves via #{userHome.selectItems}.
After the conversion, JSF will check whether the converted instance can be found in that list using the equals() method.
You've constructed the SelectItem the wrong way. As per the class' documentation, the 1st argument should represent the item value (which is to be converted and submitted) and the 2nd argument should represent the item label (which is to be displayed in list). But you specified them the other way round.
Fix it accordingly:
selectItems.add(new SelectItem(user, user.getName()));
If that still doesn't fix the problem, then it means that the equals() method of User class is not (properly) implemented. JSF will use it to validate the selected User against any of the item values of the list after conversion.
Unrelated to the concrete problem, it may be useful to know that <f:selectItems> in JSF2 offers you the possibility to build the list without the need to build a list of SelectItem manually. Here's an example which achieves exactly the same:
<f:selectItems value="#{userHome.availableLeaders}" var="user"
itemValue="#{user}" itemLabel="#{user.name}" />
This allows you to get rid of the additional selectItems property and the loop in the bean constructor.

How to add multiple instances of JSF 2 managed beans to Java Collection?

I'm trying to wrap my head around some basic JSF 2 concepts. For instance, if I have a managed bean, Bean1:
#ManagedBean
public class Bean1 {
private String foo;
private String bar;
}
and the values for foo and bar are obtained from a JSF web form. On each submit of the web form, I want to store an instance of Bean1 in a Java Collection of another bean:
#ManagedBean
public class Bean2 {
private List<Bean1> beanList;
}
What is the correct way to achieve this? Thanks.
BalusC is 100% per cent right, but (as he warns) his answer will be useless. The important point here is that you do not need nor want the second bean to be managed at all. It is your model, not your GUI. You probably wanted something like:
#ManagedBean
#ViewScoped
class PeopleHolder {
private List<Person> people = new ArrayList<Person>();
// not managed at all:
private Person currentPerson;
// just the getter, no need for a setter
public Person getCurrentPerson() { return currentPerson; }
#PostConstruct
public init(){ currentPerson = new Person(); }
public void addCurrentPersonToList() {
people.add(currentPerson);
init();
}
// just for test:
public List<People> getPeople() { return people; }
}
and now a form:
<h:form>
<h:inputText value="#{peopleHolder.currentPerson.name}" />
<h:inputText value="#{peopleHolder.currentPerson.lastName}" />
<h:commandButton action="#{peopleHolder.addCurrentPersonToList}" />
</h:form>
Make Bean2 a managed property of Bean1 so that you have access to its beanList property.
#ManagedBean
public class Bean1 {
private String foo;
private String bar;
#ManagedProperty("#{bean2}")
private Bean2 bean2;
public void submit() {
bean2.getBeanList().add(this);
// ...
}
// ...
}
(please note that this way just the reference is stored, not a clone of the Bean1's state or something!)
Needless to say that this is a design smell. There are likely better ways to achieve the concrete functional requirement which you've had in mind while asking the question but didn't tell anything about. In the future try to ask how to solve the functional requirement instead of how to achieve a solution (which may not be the right solution after all).

Resources