Omnifaces socket + commandScript 组合

Omnifaces socket + commandScript combinations

我想用 the <o:commandScript> option as documented here.

实现 OmniFaces

我的 JSF 页面如下所示:

<h:form id="myForm">
    <!-- the socket is by default disconnected. It will
         connect when the user selects a subChannel. 
         Setting the subChannel to "null" must disconnect
         the socket -->
    <o:socket channel="myChannel" scope="session"
              connected="#{myManager.subChannel ne null}"
              user="#{myManager.subChannel}"
              onmessage="someTestScript" onopen="onOpen" onclose="onClose"/>

    <o:commandScript name="someTestScript"
                     actionListener="#{myManager.testPush}"
                     render=":testValue" />

    <!-- dummy rendering to check if everything is working -->
    <h:panelGroup id="testValue"
                  rendered="#{myManager.subChannel ne null}" >
        <h:outputText value="#{myManager.bbb} #{myManager.aaa}" />
    </h:panelGroup>

    <!-- To simplify: I have several buttons which allows
         the user to select the socket "user". The buttons
         are only displayed when no "user" is selected 
         (thus, socket is not connected yet as well) -->
    <p:dataGrid value=#{...a list...} var="sth" 
                rendered="#{myManager.subChannel eq null">

        <p:ajax event="click"
                update="myForm"
                listener="#{myManager.setSubChannel(sth.id)}" />

        <p:panel>
            <h:outputText value=#{sth.text} />
        </p:panel> 

    </p:dataGrid>

    <!-- Resetting -->
    <p:commandButton value="Reset"
                     actionListener="#{myManager.setSubChannel(null)"
                     update="myForm" />
</h:form>

我的支持 bean:

@Named(value = "myManager")
@SessionScoped
public class MyStuffManager implements Serializable{

    private Long subChannel = null;
    private Long aaa;
    private String bbb;

    public void testPush(Long aaa, String bbb){
       System.out.println("Text received: " + bbb + ", Number received: " + aaa);
       this.aaa = aaa;    
       this.bbb = bbb;
    }

    // getter & setter

}

我的套接字推送点:

@Named 
@ApplicationScoped
public Class mySocket implements Serializable{
    @Inject
    @Push(channel = "myChannel")
    private PushContext socket;

    // Basically, all methods are triggered by CDI events. So
    // far, I just want to send an int and a String to see how
    // I send parameters.
    public void test(@Observes @MyEventQualifier AnEvent event) {
        Map<String, Object> input = new HashMap<>();
        input.put("aaa", 123456789);
        input.put("bbb", "a text");
        socket.send(input);
    }
}

目前我的情况是:

  1. 单击面板和 "reset" 按钮时,插座会正确连接和断开连接。
  2. 仍然根据链接文档, 选项正常工作,但我无法发送参数。

不过,我的开放点是:

  1. 具有 "null" 用户属性。无论我使用 "connect" 属性触发连接还是
    oncomplete="OmniFaces.Push.open('myChannel')"
    ,subChannel 仍然解析为 null。奇怪的是,如果我执行其他 JSF 内容(如更新 字段),subChannel 会正确更新和检索。 当连接不是自动时,我应该如何根据 EL 获得动态 "user" 属性?
  2. 我遗漏了 OmniFaces 文档中的一些内容,因为从未调用过 myManager.testPush。我错过了什么? 相反,我的 Firefox 控制台上有一个 ReferenceError: someTestScript is not defined。我试图在 JavaScript 文件中添加一个空的 someTestScript 但看起来只调用了一个空的,而不是 o:commandScript.

我们正在使用文档中所述的 OmniFaces 2.6。

显然欢迎任何hint/advise。谢谢。

我终于修好了!有兴趣求解的,以后自己注意:

1) 如何动态改变子频道

选项 1:将 o:socket 范围更改为视图 ,以便 HTTP 会话范围比套接字范围更宽。

<o:socket channel="myChannel" scope="view"
          connected="#{myManager.subChannel ne null}"
          user="#{myManager.subChannel}"
          onmessage="someTestScript" onopen="onOpen" onclose="onClose"/>

退出套接字时,套接字将正确关闭并使用正确的 subChannel 值重新打开。由于某些原因,将 "subChannel" 作为字符串似乎比 Long 更好。

选项 2:使用 "sub channel map"。实际上,我没有在我的问题中说明多个用户可以连接到同一个子频道。另一种选择是让所有用户通过他们的 userId <o:socket channel="myChannel" user="#{user.id}" session="scope" /*default value anyway*/ {...}/>

连接

然后,我没有发送到子频道,而是发送到用户 ID 的集合:

Map<String, Set<String>> subChannelUserMap = new HashMap<>();
// ...
public void onUserConnect(User user, String subChannel){
    subChannelUserMap.get(subChannel).add(user.getId());
}
// ....
public void sendMessage(Map<K, V> input, String subChannel){
    socket.send(input, subChannelUserMap.get(subChannel));
}

2) 从 JavaScript

调用支持 bean 方法

如我的自我评论所述,我误解了 <o:commandScript> 用法。让我们考虑支持 bean 方法:

// ...
public class BackingBean{
    public void test(){
        String aaa = Faces.getRequestParameter("aaa", String.class);
        String bbb = Faces.getRequestParameter("aSillyName", String.class);
        LOGGER.debug("aaa={}, bbb={}", aaa, bbb);
    }    
}

套接字写成<o:socket /*...*/ onmessage=onMessage />,我将有:

JSF 页面:

 <o:commandScript name="aSillyFunctionName" render="..." 
                  actionListener="#{backingBean.test}" ... />

JavaScript 文件:

 function onMessage(message, channel, event){
     aSillyFunctionName({aaa: message.aaa, aSillyName: bbb});
 }

到此结束。