jsf - Omnifaces 套接字 + commandScript 组合

标签 jsf websocket omnifaces

我想使用 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. 单击面板和“重置”按钮时, socket 会正确连接和断开连接。
  2. 仍然根据链接文档, 选项可以正常工作,但我无法发送参数。

但是,我的开放点是:

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

按照文档所述,我们正在使用 OmniFaces 2.6。

显然欢迎任何提示/建议。谢谢。

最佳答案

我终于修好了!对于那些对解决感兴趣的人,请稍后为自己做笔记:

1)如何动态更改子 channel

选项 1:更改 o:socket 范围以查看,使 HTTP session 范围比套接字范围更宽。

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

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

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

然后,我不发送到子 channel ,而是发送到用户 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 方法

正如我的 self 评论中所述,我误解了<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});
 }

到这里我就完成了。

关于jsf - Omnifaces 套接字 + commandScript 组合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42893528/

相关文章:

java - 从另一个类调用字符串时出现问题 - 变量可能尚未初始化

java - 排序 h :dataTable

html - html页面保留字符串样式

javascript - 通过 websocket 接收文件并启动下载对话框

websocket - HTTP/2 是否会使 websockets 过时?

jsf-2 - Primefaces SelectOneMenu 与列表中的对象会在选择时翻转回来

linux - 丢失的 JSF 初学者 : Linux, Geronimo 和标签库

php - 使用php将数据推送到websocket服务器

validation - 将参数传递给 o :validateOrder OmniFaces 2. 0 中的资源文件

jsf - java.io.NotSerializedException : org.omnifaces.taghandler.Converter