Uploaded image for project: 'RichFaces'
  1. RichFaces
  2. RF-13879

Duplicate component ID when using own composite with ui:include

    Details

    • Steps to Reproduce:
      Hide
      1. Deploy the attached DuplicateIdDemo.war file with WildFly 8.1
      2. Open the index.jsf location, e.g. http://localhost:8080/DuplicateIdDemo/index.jsf
      3. Select the first entry in the datatable.
      4. Observe the IllegalStateException in the server.log
      Show
      Deploy the attached DuplicateIdDemo.war file with WildFly 8.1 Open the index.jsf location, e.g. http://localhost:8080/DuplicateIdDemo/index.jsf Select the first entry in the datatable. Observe the IllegalStateException in the server.log
    • Workaround Description:
      Hide

      Use Mojarra 2.1.7 instead

      Show
      Use Mojarra 2.1.7 instead

      Description

      First of all, I must admit, that this issue is unfortunately difficult to describe and to setup, but I tried everything and was not able to solve this situation on my own. This said, I try my very best to explain our problem:

      When updating the application server of our web-application from JBoss AS 7.1 to WildFly 8.1 also the Mojarra JSF implementation has been updated from 2.1.7, to 2.2.6. Exactly this update is causing troubles now.

      Of course some might say, than this is actually a Mojarra bug, but I don't think so, since I only could reproduce it when using an own composite, a dynamic <ui:include>, a <rich:tabPanel> and a <rich:extendedDataTable> in combination. An IllegalStateException is being thrown once switching the included content.

      15:09:01,652 SEVERE [javax.enterprise.resource.webcontainer.jsf.context] (default task-5) java.lang.IllegalStateException: 
      Component ID j_idt4:myComponent3:composition has already been found in the view. 
      	at com.sun.faces.util.Util.checkIdUniqueness(Util.java:987)
      	...
      	at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:219)
      	at javax.faces.webapp.FacesServlet.service(FacesServlet.java:647)
      	...
      

      Background

      For each entry in the <rich:extendedDataTable> we have an own editor, which will be included dynamically via <ui:include>. Now, if one changes the selection in the datatable (to an element which is then including a "new" XHTML) the IllegalStateException is being thrown.
      The main source of the issue is the own composite, here <my:example>, which is used at three places, but with different IDs (index.html, include1.xhtml and include2.xhtml). And the really strange thing is, that with Mojarra 2.1.7 it's working like a charm.

      /index.xhtml
      ...
      <rich:tabPanel>
      	
      	<rich:tab header="tab#1">
      		<rich:extendedDataTable value="#{controller.sampleData}"
      			var="data" selectionMode="single"
      			selection="#{controller.selection}">
      
      			<rich:column width="400px">
      				<f:facet name="header">Column</f:facet>
      				<h:outputText value="#{data}" />
      			</rich:column>
      
      			<a4j:ajax event="selectionchange"
      				listener="#{controller.selectionListener}"
      				render="includePanel" />
      		</rich:extendedDataTable>
      
      		<h:panelGroup id="includePanel">
      			<h:panelGroup
      				rendered="#{controller.selectedEntry != null}">
      				<ui:include src="#{controller.selectedEntry.includePath}" />
      			</h:panelGroup>
      			<h:panelGroup
      				rendered="#{controller.selectedEntry == null}">
      				<h:outputText value="No entry selected!" />
      			</h:panelGroup>
      		</h:panelGroup>
      	</rich:tab>
      	
      	<rich:tab header="tab#2">
      		<h:panelGroup id="includePanel2">
      			<my:example id="myComponent3" />
      		</h:panelGroup>
      	</rich:tab>
      	
      </rich:tabPanel>
      ...
      
      /include1.xhtml
      <ui:composition xmlns="http://www.w3.org/1999/xhtml"
      	...
      	<h:panelGroup id="includedPanel1" layout="block">
      		<h1>Include#1</h1>
      		<h:outputText
      			value="Selected data: #{controller.selectedEntry.data}">
      		</h:outputText>
      		<p>
      			<my:example id="myComponent1" />
      		</p>
      	</h:panelGroup>
      </ui:composition>
      
      /include2.xhtml
      <ui:composition xmlns="http://www.w3.org/1999/xhtml"
      	...
      	<h:panelGroup id="includedPanel2" layout="block">
      		<h1>Include#2</h1>
      		<h:outputText
      			value="Selected data: #{controller.selectedEntry.data}">
      		</h:outputText>
      		<p>
      			<my:example id="myComponent2" />
      		</p>
      	</h:panelGroup>
      </ui:composition>
      
      /resources/components/example.xhtml
      <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:composite="http://java.sun.com/jsf/composite"
      	xmlns:c="http://java.sun.com/jsp/jstl/core"
      	xmlns:fn="http://java.sun.com/jsp/jstl/functions"
      	xmlns:ui="http://java.sun.com/jsf/facelets">
      
      <composite:interface>
      	<composite:attribute name="id" required="false" />
      </composite:interface>
      
      <composite:implementation>
      
      	<h:panelGroup id="composition">
      		<h:outputText value="Last access: #{session.lastAccessedTime}" />
      	</h:panelGroup>	
      
      </composite:implementation>
      </html>
      
      Controller.java
      import java.io.Serializable;
      import java.util.ArrayList;
      import java.util.Collection;
      import java.util.List;
      
      import javax.enterprise.context.SessionScoped;
      import javax.faces.event.AjaxBehaviorEvent;
      import javax.inject.Named;
      
      import org.richfaces.component.UIExtendedDataTable;
      
      @Named
      @SessionScoped
      public class Controller implements Serializable {
      
      	public class EmbeddedData {
      
      		private String data;
      
      		private String includePath;
      
      		public String getData() {
      			return data;
      		}
      
      		public String getIncludePath() {
      			return includePath;
      		}
      
      		@Override
      		public String toString() {
      			return data;
      		}
      	}
      
      	private static final long serialVersionUID = 1L;
      
      	private static final String FIRST_INCLUDE = "include1.xhtml";
      
      	private static final String SECOND_INCLUDE = "include2.xhtml";
      
      	private EmbeddedData selectedEntry;
      
      	private Collection<Object> selection;
      
      	public List<EmbeddedData> getSampleData() {
      		List<EmbeddedData> data = new ArrayList<>();
      		for (int i = 0; i < 3; i++) {
      			EmbeddedData d = new EmbeddedData();
      			String s = "Data #" + i + " using include " + FIRST_INCLUDE;
      			d.data = s;
      			d.includePath = FIRST_INCLUDE;
      			data.add(d);
      		}
      		for (int i = 3; i < 6; i++) {
      			EmbeddedData d = new EmbeddedData();
      			String s = "Data #" + i + " using include " + SECOND_INCLUDE;
      			d.data = s;
      			d.includePath = SECOND_INCLUDE;
      			data.add(d);
      		}
      		return data;
      	}
      
      	public EmbeddedData getSelectedEntry() {
      		return selectedEntry;
      	}
      
      	public void selectionListener(AjaxBehaviorEvent event) {
      		UIExtendedDataTable dataTable;
      		dataTable = (UIExtendedDataTable) event.getComponent();
      		Object originKey = dataTable.getRowKey();
      		for (Object selectionKey : this.selection) {
      			dataTable.setRowKey(selectionKey);
      			if (dataTable.isRowAvailable()) {
      				Object rowdata = dataTable.getRowData();
      				this.selectedEntry = (EmbeddedData) rowdata;
      			}
      		}
      		dataTable.setRowKey(originKey);
      	}
      
      	public Collection<Object> getSelection() {
      		return selection;
      	}
      
      	public void setSelection(Collection<Object> selection) {
      		this.selection = selection;
      	}
      
      }
      

      Conclusion

      With the attached .war file I tried to rebuild our application architecture in the most simple way. Of course I would appreciate any advice or workaround, for our "dynamic editor" approach to avoid <ui:include>, but I couldn't find any besides it.

        Gliffy Diagrams

          Attachments

            Issue Links

              Activity

                People

                • Assignee:
                  Unassigned
                  Reporter:
                  res_offz Tobias Seppenhauser
                • Votes:
                  0 Vote for this issue
                  Watchers:
                  2 Start watching this issue

                  Dates

                  • Created:
                    Updated:
                    Resolved: