1

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.

Community
  • 1
  • 1
Murat Derya Özen
  • 2,154
  • 8
  • 31
  • 44

4 Answers4

6

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.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • I got most of it working, thanks to you. I've updated my question. Would you be able to provide some more insight regarding my second question? And thanks for the tip on my design, I changed it accordingly. – Murat Derya Özen Jan 10 '12 at 07:18
  • You're welcome. Please ask separate questions in separate questions. – BalusC Jan 10 '12 at 11:53
  • I did: http://stackoverflow.com/questions/8803032/jsf2-0-view-not-updating-after-successful-ajax-call-primefaces – Murat Derya Özen Jan 10 '12 at 12:11
  • I now completed furnishing my design by considering what you said about the abuse of the model class. Just a quick question on your point with `UserService` being a `ManagedBean`. It is not a `ManagedBean` anymore, but the `"userService"` is stored in a session. Since it is not a `Session scoped managed bean` anymore, I initialize it in `Users#init()` like this: `userService = UserService.getCartServiceFromSession((HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(true));` – Murat Derya Özen Jan 10 '12 at 20:51
  • where `public static UserService#getUserServiceFromSession` looks up the session and returns the appropriate session attribute, or creates one if necessary. Can you please comment on my approach if that's any better? :) Btw, I did not create a new question for this since it's a follow-up to your specific comment. Best, – Murat Derya Özen Jan 10 '12 at 20:52
  • I didn't mean to say that this is completely wrong, just that it's not completely right. But on Tomcat you don't have much other choice. You can just keep it a session scoped managed bean, you only need to ensure that you don't reference it from the view side. Manually getting/putting in the `HttpSession` is actually more worse :) (this is actually which the JSF `@SessionScoped` already is doing under the covers) – BalusC Jan 10 '12 at 20:59
  • I know, but JSF isn't doing it for me anymore because I removed `@ManagedBean` and `@SessionScoped`, so that it's not available in the view. That's why I felt the need to set it manually. Why is it worse, what's the reason? And would `@SessionScoped` work fine without `@ManagedBean`? – Murat Derya Özen Jan 10 '12 at 21:08
  • Manually putting in session doesn't prevent it from being available to the view. It's only uglier than using `@SessionScoped` (ideally, you'd like to avoid `javax.servlet` imports in your JSF code). Just keep it as you originally had. No it won't work without `@ManagedBean`. Either stick to that, or enrich Tomcat with EJB or CDI support, or look for another container like Glassfish Web Profile. – BalusC Jan 10 '12 at 21:13
  • I switched to WebLogic 12.1 because of this bug and I guess it has support for `EJBs`. I annotated the `cartService` attribute with `@EJB` and the `CartService` class with `@Stateful`. As expected, such trial-errors do not go well with JavaEE :) Do you happen to know a decent and simple tutorial on how I can solve my problem by utilizing EJBs? – Murat Derya Özen Jan 10 '12 at 21:26
  • That should be sufficient. What happens instead? – BalusC Jan 10 '12 at 21:35
  • I get the following exception: `javax.el.PropertyNotFoundException: users.xhtml @24,39 value="#{users.user.friendList}": Target Unreachable, 'null' returned null`. This is happens because the line `user = userService.getCart(merchantId);` doesn't execute normally (a `System.out.println()` prints something right above that line, but that line causes a problem). – Murat Derya Özen Jan 10 '12 at 21:42
  • Well, I smell a new question. Try to be more specific about "a problem" at least. – BalusC Jan 11 '12 at 00:21
  • 1
    They've *finally* fixed it: https://issues.apache.org/bugzilla/show_bug.cgi?id=52445 It will be available in Tomcat 7.0.24. – BalusC Jan 13 '12 at 20:32
1

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...

1

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!

Neo
  • 1,554
  • 2
  • 15
  • 28
  • 1
    Actually Tomcat 7 ships with EL 2.2 – mrembisz Jan 09 '12 at 21:10
  • Implementation provided in Tomcat 7 is Jasper EL.. It has issues.. – Neo Jan 09 '12 at 21:21
  • And sorry about the blunder @ "Tomcat does not support EL 2.2 yet" in my answer. Will change it. :) – Neo Jan 09 '12 at 21:22
  • @Neo - Thanks, I'll try it as soon as I can. Do you happen to know if WebLogic 12.1 suffers from the same issue? – Murat Derya Özen Jan 09 '12 at 21:23
  • I am not sure @Murat.. I think Weblogic uses the Glassfish EL implementation (which is the one mentioned in my answer above).. You can check to make sure. – Neo Jan 09 '12 at 21:27
  • Tomcat 7 is a Servlet 3.0 container which ships with EL 2.2 already. Those articles are targeted on Tomcat 6. – BalusC Jan 10 '12 at 03:37
0
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> 
John Suma
  • 7
  • 4
  • Please read this [how-to-answer](http://stackoverflow.com/help/how-to-answer) and follow the guideline there to answer. Just post the code is not a good answer. – thewaywewere May 07 '17 at 11:39