3

Using JSF 2.1, Mojarra 2.1.3, Glassfish 3.1.1, and PrimeFaces 3.3.1

I'm trying to handle processing field level security in JSF, on the preRenderView event, and having issues when needing to add a dynamic component to the JSF component tree. On first render, everything is fine and field level security is processed. After any sort of update, however, Mojarra complains about a duplicate id even though, through printing to the console, my add code is only running once per update.

It appears as if Mojarra isn't clearing the component tree for postbacks and thus each subsequent rendered update adds an additional version of the component to the tree.

Thanks for any help anyone can provide. Here's some dumbed-down sample code. On commandButton click the error is thrown.

index.xhtml:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:p="http://primefaces.org/ui"
        >
    <f:event listener="#{lifeCycle.event}" type="preRenderView" />
    <h:body>
        <h:form id="form" prependId="false">
            <h:panelGroup id="testPanel">
                <h:inputText id="viewSec" value="viewSec node"/><br/>
            </h:panelGroup>
            <p:commandButton update="testPanel"/>
        </h:form>
    </h:body>
</html>

LifeCycle.java:

package com.dynamic.test;

import java.io.Serializable;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.component.html.HtmlOutputText;
import javax.faces.component.visit.VisitContext;
import javax.faces.context.FacesContext;
import javax.faces.event.ComponentSystemEvent;

@ManagedBean
@RequestScoped
public class LifeCycle implements Serializable {
    public void event(ComponentSystemEvent event){
        FacesContext facesContext = FacesContext.getCurrentInstance();
        UIViewRoot root = facesContext.getViewRoot();
        NodeInspector visitCallback = new NodeInspector();

        root.visitTree(VisitContext.createVisitContext(FacesContext.getCurrentInstance()), visitCallback);

        List<UIComponent> securityEnabledComponents = visitCallback.getSecurityEnabledComponents();
        for (UIComponent securityEnabledComponent : securityEnabledComponents) {

            if(securityEnabledComponent.getClientId().equals("viewSec")){
                List<UIComponent> childList = securityEnabledComponent.getParent().getChildren();
                int targetPosition = securityEnabledComponent.getParent().getChildren().indexOf(securityEnabledComponent);

                HtmlOutputText outputTextComponent = new HtmlOutputText();
                outputTextComponent.setId(securityEnabledComponent.getId());
                outputTextComponent.setValue(securityEnabledComponent.getAttributes().get("value"));

                childList.set(targetPosition, outputTextComponent);
            }
        }
    }
}

NodeInspector.java

package com.dynamic.test;

import java.util.ArrayList;
import java.util.List;
import javax.faces.component.UIComponent;
import javax.faces.component.visit.VisitCallback;
import javax.faces.component.visit.VisitContext;
import javax.faces.component.visit.VisitResult;
import javax.faces.context.FacesContext;

public class NodeInspector implements VisitCallback {
    private List<UIComponent> securityEnabledComponents = new ArrayList<UIComponent>();
    FacesContext facesContext = FacesContext.getCurrentInstance();

    @Override
    public VisitResult visit(final VisitContext context, final UIComponent target) {
        if(target.getClientId().equals("viewSec")){
            securityEnabledComponents.add(target);
        }
        return VisitResult.ACCEPT;
    }

    public List<UIComponent> getSecurityEnabledComponents() {
        return securityEnabledComponents;
    }
}

Error:

SEVERE: JSF1007: Duplicate component ID viewSec found in view.
    ...  +id: j_idt2
       type: <html xmlns="http://www.w3.org/1999/xhtml">

      +id: j_idt3
       type: javax.faces.component.UIOutput@17098e7
        +id: form
         type: javax.faces.component.html.HtmlForm@14677de
          +id: testPanel
           type: javax.faces.component.html.HtmlPanelGroup@167ab25
            +id: viewSec  <===============
             type: javax.faces.component.html.HtmlOutputText@14a3d2e
            +id: viewSec  <===============
             type: javax.faces.component.html.HtmlOutputText@f6a607
            +id: j_idt4
             type: <br/>

          +id: j_idt5
           type: org.primefaces.component.commandbutton.CommandButton@1380fc5
      +id: j_idt6
       type: 
    </html>...

    SEVERE: Error Rendering View[/index.xhtml]
    java.lang.IllegalStateException: Component ID viewSec has already been found in the view.  
        at com.sun.faces.util.Util.checkIdUniqueness(Util.java:821)...
  • 4
    This morning I just figured out what was causing my issue - the version of Mojarra I was running on has a TON of bugs, related to modifying the tree dynamically, that have since been mostly solved by more recent versions ([see bug report here](http://java.net/jira/browse/JAVASERVERFACES-1826)). In the process of fixing the bugs, they seemed to have come across the same issue I did. Modifying the tree causes tons of orphaned nodes to be left sitting around. These nodes are not actually in the rendered HTML but id uniqueness checked none the less. – Nick Weddleton Dec 04 '12 at 14:51
  • 2
    You should post this comment as an answer. It's useful to know. – KidTempo Jan 27 '13 at 14:53

1 Answers1

-1

Check if the version of Mojarra you are using is compatible with the Primefaces 3.3.1.
Use latest version of Primefaces 3.6.

Kishor Prakash
  • 8,011
  • 12
  • 61
  • 92