4

I have tried to taste the new features of JSF 2.3, one attractive is the websocket.

I have read some sample codes from mojarra tests and JSF 2.3 specific @Push javadoc.

And encountered some issues when used f:websocket and f:ajax.

The facelets template is:

   <h:panelGroup id="messagePanel" layout="block">
        <ul>
            <ui:repeat value="#{ajaxBean.messages}" var="m">
                <li>#{m}</li>
            </ui:repeat>
        </ul>
    </h:panelGroup>

    <h:form id="form">
        <h:commandButton 
            id="sendMessage" 
            action="#{ajaxBean.sendMessage()}" 
            value="Send Ajax Message">
            <f:ajax/>
        </h:commandButton>
    </h:form>
    <h:form>
        <f:websocket channel="ajaxChannel" scope="view">
            <f:ajax event="ajaxEvent" render=":messagePanel" />
        </f:websocket>
    </h:form>
    <h:form>
        <f:websocket channel="ajaxListenerChannel" scope="view">
            <f:ajax event="ajaxListenerEvent" listener="#{ajaxBean.ajaxPushed}" render=":messagePanel" />
        </f:websocket>
    </h:form>

    <f:websocket channel="commandScriptChannel" scope="view" onmessage="onCommandScript"/>
    <h:form>
        <h:commandScript name="onCommandScript" action="#{ajaxBean.commandScriptExecuted()}" render=":messagePanel"/>
    </h:form>

And backend bean is:

@ViewScoped
@Named("ajaxBean")
public class AjaxBean implements Serializable {

    private static final Logger LOG = Logger.getLogger(AjaxBean.class.getName());

    @Inject
    @Push
    PushContext ajaxChannel;

    @Inject
    @Push
    PushContext ajaxListenerChannel;

    @Inject
    @Push
    PushContext commandScriptChannel;

    private List<String> messages = new ArrayList<>();

    public void ajaxPushed(AjaxBehaviorEvent e) throws AbortProcessingException{
        LOG.log(Level.INFO, "ajax pushed: " + e.toString());

        messages.add("ajaxListenerEvent is sent at: " + LocalDateTime.now());

        ajaxListenerChannel.send("ajaxListenerEvent");
    }

    public void commandScriptExecuted() {
        LOG.log(Level.INFO, "commandScriptExecuted pushed.");

        messages.add("commandScriptExecuted message is sent at: " + LocalDateTime.now());

        commandScriptChannel.send("onCommandScript");
    }

    public void sendMessage() {
//        LOG.log(Level.INFO, "ajax pushed by button: " + e.toString());

        messages.add("ajaxEvent is sent at: " + LocalDateTime.now());

        ajaxChannel.send("ajaxEvent");
    }

    public List<String> getMessages() {
        return messages;
    }

    public void setMessages(List<String> messages) {
        this.messages = messages;
    }

}

The result is the first button can trigger the ajax output as needed. But f:ajax with a listener does not work, and h:commandScript also does not work here.

How to correct these?

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
Hantsy
  • 8,006
  • 7
  • 64
  • 109
  • But you're nowhere in your code explicitly pushing to those channels? Moreover, those channels are only pushed by their own ajax/command listener! I don't understand what you're trying to achieve. – BalusC Oct 02 '17 at 17:59
  • @BalusC I added **push** action in the f:ajax `listener` and h:commandScript `action`. The `@Push` javadoc explains use f:websocket with f:ajax to handle complex UI. What I want to use f:webocket and f:ajax or h:commandScript is to execute the backend action and append content automatically. Can I archive this purpose? or I misunderstand the f:webscoket/f:ajax usage. – Hantsy Oct 03 '17 at 01:00
  • @BalusC I have updated the [codes](https://github.com/hantsy/ee8-sandbox/tree/master/jsf-websocket), if use an extra button to push the ajax signal to channel, it works. – Hantsy Oct 03 '17 at 01:23

1 Answers1

5

Your concrete problem is caused because you're nowhere in your code explicitly sending a push message to those websockets. If your attempt were possible in some way, the websocket would keep sending a push message to itself in an infinite loop. This doesn't make sense.

In order to explicitly send a push message, you have to let your code explicitly call push.send(message), exactly as you already did with that <h:commandButton>.

<h:form>
    <h:commandButton value="Send push message" action="#{bean.sendPushMessage}">
        <f:ajax />
    </h:commandButton>
</h:form>
<h:form>
    <f:websocket channel="pushWithAjaxUpdate" scope="view">
        <f:ajax event="updateMessages" listener="#{bean.updateMessages}" render=":messages" />
    </f:websocket>
</h:form>
<h:panelGroup id="messages" layout="block">
    <ul>
        <ui:repeat value="#{bean.messages}" var="message">
            <li>#{message}</li>
        </ui:repeat>
    </ul>
</h:panelGroup>

@Inject @Push
private PushContext pushWithAjaxUpdate;

public void sendPushMessage() {
    messages.add("Push message is sent at: " + LocalDateTime.now());
    pushWithAjaxUpdate.send("updateMessages");
}

public void updateMessages() {
    messages.add("Ajax event is received at: " + LocalDateTime.now());
}

I understand that you're just experimenting, but it should be said that above approach wouldn't make sense in real world code either. The <f:websocket> is superfluous in this specific use case and it would suffice to just use <h:commandButton><f:ajax listener="...">. It's in real world code expected that the push is not synchronously invoked by some command button in the very same page, but that it is asynchronously invoked by some business event. Otherwise you could as good just return the result in the ajax response itself. This saves an additional round-trip to the server.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • I'm trying the very same example and I cant reRender messages component. The only way to make it work was, `` and a JS script writing html basic logic – Germán Faller Jun 23 '19 at 02:08
  • In my case was wrong dependency ` org.wildfly wildfly-jsf 15.0.0.Final ` – Germán Faller Jun 23 '19 at 03:42
  • How can i do it **without commandButton**? I sent a push message using observer method invoked by observable bean. But nothing pushed. – Arash Aug 05 '20 at 18:55