I am developing a JSF custom component. This component has the purpose of encapsulating another component (namely a PrimeFaces table) and adding customized behaviour to it.
JSF state management remembers dynamic manipulations to the component tree so that it can ensure that it's after the restore view of a postback exactly the same as it was during render response of the previous request which generated the post form.
The exception which you got,
javax.faces.FacesException: Cannot remove the same component twice: table:additional
basically tells that you've added the very same component twice to the same parent.
In other words, you obtained the desired component from the component tree and then added it to the desired parent. However, as per the given exception, it was at that moment already attached to the desired parent! Effectively, you performed a no-op. But JSF remembers every single dynamic component add/remove through postbacks on the same view, even though it's effectively a no-op. That part may in turn be a bug in JSF implementation itself, but you should in first place not be moving around components when they are aready in the desired place.
A quick fix would be to check the component's parent by UIComponent#getParent() if it isn't already the desired one and if so then skip the getChildren().add() call.
if (!componentToMove.getParent().equals(targetParent)) {
targetParent.getChildren().add(componentToMove);
}
A hack would be to set UIComponent#setInView() to false so that JSF doesn't remember the dynamic action.
componentToMove.setInView(false);
targetParent.getChildren().add(componentToMove);
componentToMove.setInView(true);
// NOTE: with MyFaces, call setInView() on componentToMove.getParent() instead.
Be however cautious when using this method, see also its javadoc.
However, the most natural way to perform component tree manipulations is a postAddtoViewEvent listener instead of during encodeXxx() method.
@ListenerFor(systemEventClass=PostAddToViewEvent.class)
public class YourComponent extends SomeUIComponent {
@Override
public void processEvent(ComponentSystemEvent event) {
if (event instanceof PostAddToViewEvent) {
targetParent.getChildren().add(componentToMove);
}
}