I finally managed to get this working. The JSF Component which I presented in question should have three classes: Component, Renderer, and ComponentHandler.
The child UICommandLink should be added to HintBubbleComponent in its ComponentHandler in method on Component created:
public class HintBubbleHandler extends ComponentHandler {
public HintBubbleHandler(ComponentConfig config) {
super(config);
}
@Override
public void onComponentCreated(FaceletContext ctx, UIComponent c, UIComponent parent) {
HintBubbleComponent hintBubbleComponent = (HintBubbleComponent) c;
UICommandLink commandLink = new UICommandLink();
hintBubbleComponent.getChildren().add(commandLink);
commandLink.setParent(hintBubbleComponent);
commandLink.setId("okButton");
}
}
Thanks to that, UICommandLink will be present in the component tree when the tree is visited by callback looking for component which executed action.
The actionListener should be set to UICommandLink in decode() method of the renderer:
@Override
public void decode(FacesContext context, UIComponent component) {
super.decode(context, component);
UICommandLink commandLink = (UICommandLink) component.getChildren().get(0);
MethodExpression actionListener = ((HintBubbleComponent) component).getActionListener();
if ( actionListener != null ) {
commandLink.setActionExpression(actionListener);
}
}
Now everything is working at the action is invoked !