Using JSF 2.0 and EL, I am trying to call a method on a POJO which is an attribute of a viewscoped bean. The code is actually very similar to @BalusC's tutorial here. When I call a method that takes no arguments, everything's fine. But when I try to call a method that takes an argument, I get the following exception:
javax.faces.el.MethodNotFoundException: javax.el.MethodNotFoundException:
/user.xhtml at line 42 and column 32 action="#{users.user.removeFriend(friend)}":
Method not found: model.User@67f2b0dd.removeFriend()
Here are some more details:
user.xhtml
<f:metadata>
<f:viewParam name="id" value="#{users.id}" />
<f:event type="preRenderView" listener="#{users.init}" />
</f:metadata>
...
<h:form id="usersForm">
<p:outputPanel>
<p:dataTable id="userTable" value="#{users.user.friendList}" var="friend">
<p:column>
<h:outputText value="#{friend.name}" />
</p:column>
<p:column>
<p:commandButton action="#{users.user.removeFriend(friend)}"
ajax="true"
update="userTable somethingElse" process="@this"
onerror="errorDialog.show();"
icon="ui-icon-delete"
title="delete user">
</p:commandButton>
</p:column>
</p:dataTable>
</p:outputPanel>
<p:commandButton action="#{users.user.removeAllFriends()}" ajax="true"
update="userTable somethingElse"
process="@this"
icon="ui-icon-close"
value="delete all friends?">
</p:commandButton>
</h:form>
I have the following ViewScoped
bean:
Users.java
@ManagedBean(name = "users")
@ViewScoped
public class Users implements Serializable {
private static final long serialVersionUID = 1L;
private String id;
private User user;
@ManagedProperty("#{userService}")
private UserService userService; // session scoped
public void init() {
user = userService.getCart(id);
}
public final String getId() {
return id;
}
public final void setId(String id) {
this.id= id;
}
public final User getUser() {
return user;
}
public final void setUser(User user) {
this.user= user;
}
public final void setUserService(UserService userService) {
this.userService = userService;
}
}
The User
class - a POJO - has a private List<Friend> friends
attribute, with getter and setters and a public
method User#removeFriend(Friend f)
. It has another public
method; User#removeAllFriends()
.
The page renders fine but I get the exception when I click the "Remove" commandButton next to a user in the table.
What's wrong here? Why can I successfully call a parameter-less method but can't pass arguments to another?
Edit: The application is deployed on Tomcat 7.0, if that's any good.
Any help appreciated.
Update: As BalusC and Neo pointed, this is an issue with Tomcat 7.0. I installed WebLogic 12.1 and it all worked fine.
This is a bug in Tomcat. It works when you call the method on the bean directly, but not when you call it on a nested property. I recall this issue as issue 50449 which I have ever reported but was closed as "works for me" (perhaps they did not test it very properly, I didn't find it worth the effort to argue with Tomcat guys again, I haven't had very good experiences with them). In any way, I have re-reported it as issue 52445 with a more solid testcase -I hope.
In the meanwhile, replacing the EL implementation with a different one, from Glassfish for example, should work out. But I can tell you that whatever you're trying to do is not really the proper approach. You've declared a business method on the model (the User
entity class) instead of on the controller (the Users
managed bean class). This is not right. The model should solely be used to hold the data. The controller should be used to hold the business actions.
I recommend to rewrite your case as follows:
<h:form id="usersForm">
<p:outputPanel>
<p:dataTable id="userTable" value="#{users.user.friends}" var="friend">
<p:column>
<h:outputText value="#{friend.name}" />
</p:column>
<p:column>
<p:commandButton action="#{users.removeFriend(friend)}"
process="@this" update="userTable somethingElse" onerror="errorDialog.show();"
icon="ui-icon-delete" title="delete user" />
</p:column>
</p:dataTable>
</p:outputPanel>
<p:commandButton action="#{users.removeAllFriends}"
process="@this" update="userTable numUsers"
value="delete all friends?" />
</h:form>
and put the business methods in the Users
managed bean instead:
public void removeFriend(Friend friend) {
userService.removeFriend(user, friend);
// ...
}
public void removeAllFriends() {
userService.removeAllFriends(user);
// ...
}
Also the UserService
being another @ManagedBean
is not entirely right. It should actually be an @Stateless
EJB, but that's another story. EJBs are not supported on Tomcat anyway without enriching it with for example OpenEJB. Without EJB, it does not necessarily need to be another managed bean. You don't want to expose those services into the view directly.
Ahh.. that explains it @ Tomcat.. The way in which you are passing the parameter "#{users.user.removeFriend(friend)}" is EL 2.2. You can overcome that by following the steps here: http://www.mkyong.com/jsf2/how-to-pass-parameters-in-method-expression-jsf-2-0/ OR by using some other way as described here: http://www.mkyong.com/jsf2/4-ways-to-pass-parameter-from-jsf-page-to-backing-bean/. Good luck!
If you are using Tomcat you can do the following.
In the xhtml file you do something like this.
#{ItemInformationController.setFindItem(525)}
#{ItemInformationController.findItem}" var="AVar">
In your controller file you can do something like this:
int itemId;
public List<Item> getFindItem() {
return getJpaController().findLevel3Item(levelId);
}
public void setFindItem(int id) {
itemId= id;
}
This works find with Tomcat 6/7...
enter code here
@ManagedBean(name = "customerBean")
@SessionScoped
public class CustomerBean implements Serializable {
/**/
private static final long serialVersionUID = 1L;
CustomerManager customerManager = CustomerManager.getInstance();
private Book book;
private Long id;
private String name, vorname, addresse, ort;
Customer customer = new Customer();
ArrayList<Customer> customerList;
public void findCustomer(String name){
CustomerManager.getInstance().findCustomerByName(name);
System.out.println("Hello" + customer.getName());
}
getters and setters...
public class CustomerManager {
static EntityManagerFactory emf;
static EntityManager em;
static CustomerManager instance;
EntityTransaction entityTransaction = null;
public CustomerManager() {
emf = Persistence.createEntityManagerFactory("customerRegistration");
em = emf.createEntityManager();
}
List<Customer> customerstList = new ArrayList<Customer>();
Book book = new Book();
Set<Book> bookList = new HashSet<Book>();
Customer customer = new Customer();
public void findCustomerByName(String name) {
// Query for a single object.
Query query = em.createQuery("Select c FROM Customer c WHERE c.name = :name");
query.setParameter("name", name);
System.out.println("Hello from Business");
customer = (Customer)query.getSingleResult();
}
<ui:define name="content">
<h:body>
<h:form id="main">
<p:panelGrid columns="2">
<p:outputLabel for="name" value="#{texts.name}" />
<p:inputText id="name" value="#{customerBean.customer.name}" />
<h:commandButton value="Search"
action="#{customerBean.findCustomer()}" />
</p:panelGrid>
</h:form>
</h:body>
</ui:define>