2

I have the following button:

   <h:commandButton 
     disabled="#{mybean.searching}"
     binding="#{mybean.searchButton}"
     actionListener="#{mybean.searchForLicenses}"
     value="Search" />

When I debug I see that the actionListener is called twice first, then three times, next click four times and so on.

It seems like on every reload the actionListener is registered one more time.

I'm using Mojarra 2.1.3 (also tried 2.0.6) and Tomcat 7 with IceFaces.

The binding is done that way:

private javax.faces.component.UICommand searchButton;

public void setSearchButton(UICommand searchButton) {
  this.searchButton = searchButton;
}

public UICommand getSearchButton() {
  return searchButton;
}
mrembisz
  • 12,722
  • 7
  • 36
  • 32
hugri
  • 1,416
  • 3
  • 18
  • 32
  • How is the binding done? Can you post get/setSearchButton with all related code? – mrembisz Nov 08 '11 at 13:05
  • The problem goes away when I remove the binding. @mrembisz: I added the binding code (sorry it's not formatted because I get a virus warning on the page preventing the editor to load). – hugri Nov 08 '11 at 13:23
  • 1
    When you specify binding, your component will be reused if holding bean remains in scope. There can be many actionListeners on a single component, so with each request a new listener was registered with your command. – mrembisz Nov 08 '11 at 15:07

2 Answers2

8

That can happen if you've bound the component to a session or application scoped bean instead of a request scoped bean. This is simply a bad design. The very same component would be reused among multiple requests/views. You need to put the bean in the request scope, or to get rid of the component binding altogether.

Note that binding the component directly to a bean is often a sign of poor design somewhere in the code. What is it, the functional requirement and/or problem for which you thought that this is the solution? If you elaborate on that, we may be able to propose the right approach.

Also note that using an action listener alone is also a design smell. I'd expect "searchForLicenses" to be a normal action method. See also Differences between action and actionListener.

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • I guess you're right. Still it's not very obvious for me that you can't use a session scoped bean in combination with component binding and actionListener. Why it isn't just checked if the listener has already been added to the component? Probably I'm thinking in the wrong direction here but as it's perfectly possible syntax-wise it shouldn't end up in such a mess... imho at least. – hugri Nov 08 '11 at 14:55
  • The Java or IDE compiler can't prevent "bad designs". It can at highest prevent syntax or logic errors. – BalusC Nov 08 '11 at 15:00
  • Fully agreed, still it's not obvious that it's working that way (to me). Also, when I'm using view scope, don't we end up in the same mess because on every reload the listeners are re-added since the beans are held as long as the users stays on the same page ie. view, right? – hugri Nov 08 '11 at 15:19
  • No, listeners aren't re-added because the component will be re-created. This is by the way in turn caused by a chicken-egg bug in Mojarra's partial view state saving. – BalusC Nov 08 '11 at 15:25
  • I'm confused :) correct behaviour would be to re-add listeners but it's not done that way because of a Mojarra bug? – hugri Nov 08 '11 at 15:29
  • Using `binding` in combination with view scope is broken, yes. A different bean instance will be constructed on every restore view. This is **not** related to your particular problem, but this bug makes it possible for you to use `binding` in combination with view scope the way you'd expect (which is not a good idea in any way; you also never mentioned about the functional requirement, so I can never propose the right way to achieve your functional requirement). – BalusC Nov 08 '11 at 15:51
  • Here's one example where I use listeners (it's different to the one from my original question because it's harder to replace with action there): I have a wizard for creating objects (two views: one for creating, one for confirming with the possibility to go back => request or even view state is not enough here, I need(?) session scope, at least it's very convenient). Now I have a ice:selectOneMenu where I want to react on the valueChange event (for show/hide ui elements) using the valueChangeListener property. I checked now and saw that also here the events are multiplied ... – hugri Nov 08 '11 at 16:01
  • I was more asking why you need `binding`, not why you used `actionListener`. But for that part, if you aren't using `ActionEvent` argument at all and/or are doing **real business job** in the method, then it don't need to be an action listener at all. Just make it a `void` method without any arguments. – BalusC Nov 08 '11 at 16:03
  • Sorry, the functional requirement is still not clear and why you're using `binding` is also not clear. Now you mentioned about `valueChangeListener` to show/hide elements, it starts to look more like that you're still thinking too much the ajaxless JSF 1.0/1.1 way. – BalusC Nov 08 '11 at 16:07
  • That's probably true .. I'm currently porting my app from JSF 1 to JSF 2 but I start to doubt that my way of using actionListener and binding was good design in the old JSF in the first place ... – hugri Nov 08 '11 at 16:12
  • btw - thanks a lot, will do some more reading to get a better understanding how things should work in JSF 2, at least I understand now better why combining session scoped beans with component bindings is discouraged. – hugri Nov 08 '11 at 17:00
  • My purpose for using bindings: I have a complex form where the selection of a value in a drop list should cause the showing or hiding of other components (text inputs, lists ...). I achieved this through a valueChangeListener on the dropdown lists where the new value (from ChangeEvent) is used to decide if the bound components 'rendered' property is set to true or false. I still wouldn't know how this behaviour was doable without using valueChangeListeners and bound components. Would you say that in this case the usage of them is okay (because there's no business logic but only UI 'logic')? – hugri Nov 08 '11 at 18:38
  • Use `rendered` attribute in view instead. E.g. `rendered="#{bean.dropdownValue != 'some'}"` – BalusC Nov 08 '11 at 18:41
  • Can someone elaborate on BalusC's comment, "This is by the way in turn caused by a chicken-egg bug in Mojarra's partial view state saving." In some cases it seems that a component binding is useful and I agree with hubertg that jsf should know if it already created a particular listener and not create it again. I am faced with a similar issue with icefaces where in icefaces 1.8.2 JSF 1.2 component bindings and listeners were working fine but now in icefaces 2.0 and jsf 2.1 the listeners are being created multiple times –  Feb 20 '12 at 16:01
-1

The similar issue takes place when component is using binding and validator or valueChangListener and backing bean is of View, Session or Application scope. Then corresponding listeners are called many times but not once during request (+1 time with every new request).

One possible solution is to override jsf class AttachedObjectListHolder which is used for storing component listeners. Current implementation simply add new listener to component even though the same listener is already there. So the proposed fix is to check that listener does not exist before adding it.

Details of the fix you can see here

Community
  • 1
  • 1