0

The setup I'm working here is JSF2 in JBoss AS7 with Seam-2.3.

I've a manager bean that get's called via in a big template via an <a4j:commandButton> like so:

<rich:dataTable value="#{listFactory.getMyList()}" var="ll">
...
</rich:dataTable>

...
<a4j:repeat id="jobs" value="#{person.jobs}" var="job">
  <h:outputText value="#{job.name}" />
</a4j:repeat>
<a4j:commandButton value="more" immediate="true" 
  action="#{personManager.addJob}" render="jobs" limitRender="true" />

The corresponding code from personManager:

@Name("personManager")
@Scope(ScopeType.CONVERSATION)
public class PersonMgr {
  private Person person;

  @Begin(flushMode = FlushModeType.MANUAL)
  public void selectObjects() {
    // grab the person from the request if there's one
  }

  @End
  public void saveChanges() {
    em.flush();
  }

  public void addJob() {
    person.getJobs().add(new Job());
  }
}

Now what personManager does is to call person.jobs.add(new Job()) on the private, still transient entity bean person. Before it can do that, the request dies with the following stacktrace:

javax.el.ELException: /person.xhtml @412,95 value="#{listFactory.getMyList()}": org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: Person
at com.sun.faces.facelets.el.TagValueExpression.getValue(TagValueExpression.java:114) [jsf-impl-2.1.7-jbossorg-2.jar:]
at javax.faces.component.ComponentStateHelper.eval(ComponentStateHelper.java:194) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
at javax.faces.component.ComponentStateHelper.eval(ComponentStateHelper.java:182) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
at org.richfaces.component.UISequence.getValue(UISequence.java:175) [richfaces-components-ui.jar:4.3.0.Final]
at org.richfaces.component.UISequence.createExtendedDataModel(UISequence.java:109) [richfaces-components-ui.jar:4.3.0.Final]
at org.richfaces.component.UIDataTableBase.createExtendedDataModel(UIDataTableBase.java:252) [richfaces-components-ui.jar:4.3.0.Final]
at org.richfaces.component.UIDataAdaptor.getExtendedDataModel(UIDataAdaptor.java:459) [richfaces-components-ui.jar:4.3.0.Final]
at org.richfaces.component.UIDataAdaptor.setRowKey(UIDataAdaptor.java:272) [richfaces-components-ui.jar:4.3.0.Final]
at org.richfaces.component.UIDataAdaptor.visitTree(UIDataAdaptor.java:1312) [richfaces-components-ui.jar:4.3.0.Final]
at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
at javax.faces.component.UIForm.visitTree(UIForm.java:371) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
at org.richfaces.context.ExtendedPartialViewContextImpl.visitActivatorComponent(ExtendedPartialViewContextImpl.java:448) [richfaces-core-impl.jar:4.3.0.Final]
at org.richfaces.context.ExtendedPartialViewContextImpl.visitActivatorAtExecute(ExtendedPartialViewContextImpl.java:309) [richfaces-core-impl.jar:4.3.0.Final]
at org.richfaces.context.ExtendedPartialViewContextImpl.getExecuteIds(ExtendedPartialViewContextImpl.java:98) [richfaces-core-impl.jar:4.3.0.Final]
at org.richfaces.context.ExtendedPartialViewContextImpl.isExecuteAll(ExtendedPartialViewContextImpl.java:148) [richfaces-core-impl.jar:4.3.0.Final]
at javax.faces.component.UIViewRoot.processDecodes(UIViewRoot.java:929) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
at com.sun.faces.lifecycle.ApplyRequestValuesPhase.execute(ApplyRequestValuesPhase.java:78) [jsf-impl-2.1.7-jbossorg-2.jar:]
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101) [jsf-impl-2.1.7-jbossorg-2.jar:]

Debugging this, the ExtendedPartialViewContext does indeed "get" that it's supposed to only execute the commandButton component but then appears to proceed to render walk the full JSF tree? I mean, why is that list value even evaluated? It's even in a div that should evaluate to rendered=false at the time of postback. I must be missing something glaringly obvious here?

What first occured to me is that maybe pages.xml is interfering (calls selectObjects but the action has the attribute on-postback="false" set) or I need to add <a4j:keepAlive> - but since that's gone in Richfaces4 I can't really do this and shouldn't the conversation scope span AJAX requests, too?

mabi
  • 5,279
  • 2
  • 43
  • 78
  • Note that a "full tree visit" is not the same as a "full render" or so. How else should JSF find components included in `execute` other than traversing the component tree? There's no separate mapping of components by ID or so. That it's still visiting the tree while `rendered="false"` is a second question. There's at that moment no way to guarantee that it would also have been `false` during the render response of the preceding request. – BalusC Feb 28 '13 at 13:28
  • So, bottom line: I've got to ensure that all `value`s I use anywhere on the page are readily available to be processed and/or visited whenever JSF sees fit, even when limiting request processing via `execute`? I've "fixed" the error by checking whether I should use the entity in computing the `value` of the dataTable (ie only use the entityManager if the entity's persistent). But yeah, kinda counter-intuitive even if I understand the need for a full tree traversal. Still, why does it compute the value and doesn't just check for the id? – mabi Feb 28 '13 at 14:36
  • Component IDs in repeater components like `UIData` and `UIRepeat` change on a per-iteration basis (they get a row index suffix). The value is just requested to determine the size. Further, performing business logic in a getter method is bad practice. That should be done in (post)constructor or (action)listener method. – BalusC Feb 28 '13 at 14:38
  • Yeah, haven't thought of that. Want to add the "don't use getter to do business logic (even if it's _just_ getting objects from the db)" as an answer? – mabi Feb 28 '13 at 14:43

1 Answers1

0

There is a bug in RichFaces that value attribute of components extending UIDataAdaptor is evaluated on "tree visit" even if rendered=false: https://issues.jboss.org/browse/RF-11382

Try updating to RichFaces 4.3.0.Final where bug is fixed, or apply a workaround (workaround should be repeated on every component extending UIDataAdaptor):

public class UIDataTableWorkaround extends UIDataTable {
  @Override
  public void setRowKey(FacesContext facesContext, Object rowKey) {
    if (rowKey == null && getRowKey() == null) {
      return;
    }
    super.setRowKey(facesContext, rowKey);
  }
}

and in faces-config:

  <component>
    <component-type>org.richfaces.DataTable</component-type>
    <component-class>com.example.UIDataTableWorkaround</component-class>
  </component>
Andrey
  • 6,526
  • 3
  • 39
  • 58
  • Well, that's odd because I'm deploying RF4.3.0.Final with my ear. As BalusC points out, is there even a way to know the table should not be rendered when only visiting (not rendering) it - I'd also stress that the table is contained in a ``, it doesn't have a rendered attribute itself. – mabi Mar 01 '13 at 15:28
  • @mabi As I can see there is no such attribute `rendered` supported by `ui:fragment`. Try putting `rendered="false"` on the table itself, or replace `ui:fragment` by something which support the attribute (e.g. `h:panelGroup`). – Andrey Mar 01 '13 at 21:19
  • Aw, my bad. Looking at the actual code, I used a `s:div` (which does have a rendered attribute) instead of the `ui:fragment`. The table's not the only thing controlled by this attribute, so it makes sense (to me, at least) to introduce the `rendered` as high up the component tree as possible. – mabi Mar 02 '13 at 14:09