4

I have a composite component:

<composite:interface>
    <composite:attribute name="actionMethod" 
        method-signature="java.lang.String action()" required="true" />
</composite:interface>

<composite:implementation>
    <h:form>
        <h:commandButton id="captureButton" value="#{msgs.capture}" 
            action="#{cc.attrs.actionMethod}" />
    </h:form>
</composite:implementation>

and a page which is calling that composite component:

<ezcomp:captureTitle actionMethod="#{saveDecisionsBean.captureTitle}" />

and a bean which contains the action:

@Named(value="saveDecisionsBean")
@SessionScoped
public class SaveDecisionsBean extends BackingBeanBase {
    ...
    public String captureTitle() {
        ...
    }
}

Now here is my problem. When I try to run this, it says that SaveDecisionsBean doesn't have a property captureTitle. Therefore, I have to add a SaveDecisionsBean#getCaptureTitle() method. When I do this, it runs just fine. Why should I have to define this method? It says in the <composite:attribute /> that it's a method, and it's used as an action.

Here is the exact error message I'm getting:

javax.el.PropertyNotFoundException: /index.xhtml @54,86 
    actionMethod="#{saveDecisionsBean.captureTitle}": 
    The class 'com.example.persistence.SaveDecisionsBean_$$_javassist_209'
    does not have the property 'captureTitle'.

(For SEO reasons: other implementations might show class name WeldClientProxy.)

Arjan
  • 22,808
  • 11
  • 61
  • 71
Zack Marrapese
  • 12,072
  • 9
  • 51
  • 69

2 Answers2

9

I had the same problem and I found out that it was due to that my action method did throw IllegalArgumentException. Meanwhile this has been reported as a bug: Composite action method throws PropertyNotFoundException when method throws any exception.

The tricky part (at least for me) was that my app had been working fine until I moved part of the code into a Composite Component (CC). Before my app would caught the IAE and show a nice error message but when using CC, the JSF validation (or whatever...) catches this first and produce this rather confusing error message.

I verified this by using a modified version of the test code provided by BalusC (See below). The test page shows two input & submit button components. If you enter something in the text field (apart from "panic" (without quotes)), both the CC-version and the "inline" version works (watch the std output). If you enter "panic" in the "inlined" version, you'll notice the IAE as expected, but if you enter the same thing in the upper "CC-version" you'll see the PropertyNotFoundException instead. Seems that JSF gets confused by the IAE and decides that the attribute must be a property and not an action method after all... Not sure if this is a bug or a feature. Is this according to Spec, does anybody know?

So, the conclusion here is that you can't use action methods in CC with beans that throw exceptions. For me, this means that I can't use Composite Components. Sad!

Hope this helps...

/resources/components/test.xhtml

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:cc="http://java.sun.com/jsf/composite">
<cc:interface>
  <cc:attribute name="text"/>
  <cc:attribute name="action" method-signature="java.lang.String action()" required="true" />
</cc:interface>
<cc:implementation>
  <h:form>
    <h:inputText value="#{cc.attrs.text}"/>
    <h:commandButton value="submit" action="#{cc.attrs.action}" />
  </h:form>
</cc:implementation>

/test.xhtml

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:cc="http://java.sun.com/jsf/composite/components">
<h:head>
  <title>Test</title>
</h:head>
<h:body>
  <!-- text and cmd-button as cc -->
  <cc:test text="#{bean.text}" action="#{bean.submit}" />

  <hr/>

  <!-- text and cmd-buuton inline -->
  <h:form id="inline">
    <h:inputText value="#{bean.text}"/>
    <h:commandButton value="submit2" action="#{bean.submit}" />
  </h:form>
</h:body>
</html>

And last the Bean:

@ManagedBean
@RequestScoped
public class Bean {

  private String text;

  public String getText() {
    return text;
  }

  public void setText(String text) {
    this.text = text;
  }

  public String submit() {
        if (text.equalsIgnoreCase("panic")){
          throw new IllegalArgumentException("Panic!");
        }

        System.out.println(text);

        return null;
    }
}
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
Jorgen
  • 176
  • 4
  • Of course, you could add a Faces Message instead of throwing IAE: – Jorgen Sep 14 '10 at 10:51
  • FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Some error...."); But this break the separation of beans (as simple POJOs) and JSF/Faces – Jorgen Sep 14 '10 at 10:52
  • 2
    You're entirely right! This is definitely a bug. I'll report this to the JSF boys. – BalusC Sep 14 '10 at 11:29
  • Just a note, if you remove the exception and your method throw any RuntimeException JSF will throw PropertyNotFoundException exception – Guilherme Torres Castro Jun 15 '12 at 13:35
  • This is such a basic use case you'd think there would be a unit test for it. Almost two years later and I still can't create a composite component that takes a validator. – Steve Jun 23 '12 at 08:14
1

Odd, I can't reproduce this with Mojarra 2.0.2. Maybe there's something more into the code which is colliding with one or other? Or you aren't running the code you think you're running?

For the sake of completeness, I'll include the test snippets I used to try to reproduce this problem:

/resources/components/test.xhtml

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:cc="http://java.sun.com/jsf/composite">
    <cc:interface>
        <cc:attribute name="action" method-signature="java.lang.String action()" required="true" />
    </cc:interface>
    <cc:implementation>
        <h:form>
            <h:commandButton value="submit" action="#{cc.attrs.action}" />
        </h:form> 
    </cc:implementation>
</html>

/test.xhtml

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" 
    xmlns:h="http://java.sun.com/jsf/html" 
    xmlns:cc="http://java.sun.com/jsf/composite/components">
    <h:head>
        <title>Test</title>
    </h:head>
    <h:body>
        <cc:test action="#{bean.submit}" />
    </h:body>
</html>

com.example.Bean

package com.example;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;

@ManagedBean
@RequestScoped
public class Bean {

    public String submit() {
        System.out.println("submit");
        return null;
    }

}

Does the above work for you as well?

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • That works just fine for me, even using CDI. I'll have to look into it further. – Zack Marrapese Aug 16 '10 at 15:41
  • Maybe a nested form? See also [this answer](http://stackoverflow.com/questions/2118656/hcommandlink-is-not-being-invoked/2120183#2120183) for another hints. – BalusC Aug 16 '10 at 16:02
  • There's definitely not a nested form...I **do** use CDI in the bean (I inject 3 different beans from two different scopes. Also, I use required view parameters. I'm not sure if any of this makes a difference. I don't imagine it would since issues with insantiation or view parameters would throw exceptions well before the point of actually trying to call the action. – Zack Marrapese Aug 16 '10 at 16:17